mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-05 06:41:13 +02:00
Drop Python 3.4 and switch to async / await syntax for asyncio. Fixes #1425
This commit is contained in:
1 parent
8baa480b79
commit
76af98404a
119 files changed
+2432
-3084
No files matched your search
@@ -34,7 +34,6 @@ log = logging.getLogger(__name__)
|
||||
|
||||
from uuid import UUID, uuid4
|
||||
from gns3server.utils.interfaces import is_interface_up
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from ..config import Config
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
from ..utils import force_unix_path
|
||||
@@ -130,15 +129,14 @@ class BaseManager:
|
||||
|
||||
return self._config
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
tasks = []
|
||||
for node_id in self._nodes.keys():
|
||||
tasks.append(asyncio_ensure_future(self.close_node(node_id)))
|
||||
tasks.append(asyncio.ensure_future(self.close_node(node_id)))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@@ -179,8 +177,7 @@ class BaseManager:
|
||||
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def convert_old_project(self, project, legacy_id, name):
|
||||
async def convert_old_project(self, project, legacy_id, name):
|
||||
"""
|
||||
Convert projects made before version 1.3
|
||||
|
||||
@@ -199,7 +196,7 @@ class BaseManager:
|
||||
log.info("Converting old project...")
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_project_files_path, new_project_files_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_project_files_path, new_project_files_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_project_files_path, new_project_files_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move project files directory: {} to {} {}".format(legacy_project_files_path,
|
||||
new_project_files_path, e))
|
||||
@@ -212,7 +209,7 @@ class BaseManager:
|
||||
log.info("Converting old remote project...")
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_remote_project_path, new_remote_project_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_remote_project_path, new_remote_project_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_remote_project_path, new_remote_project_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move directory: {} to {} {}".format(legacy_remote_project_path,
|
||||
new_remote_project_path, e))
|
||||
@@ -226,15 +223,14 @@ class BaseManager:
|
||||
if os.path.exists(legacy_vm_working_path) and not os.path.exists(new_vm_working_path):
|
||||
try:
|
||||
log.info('Moving "{}" to "{}"'.format(legacy_vm_working_path, new_vm_working_path))
|
||||
yield from wait_run_in_executor(shutil.move, legacy_vm_working_path, new_vm_working_path)
|
||||
await wait_run_in_executor(shutil.move, legacy_vm_working_path, new_vm_working_path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not move vm working directory: {} to {} {}".format(legacy_vm_working_path,
|
||||
new_vm_working_path, e))
|
||||
|
||||
return new_id
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, name, project_id, node_id, *args, **kwargs):
|
||||
async def create_node(self, name, project_id, node_id, *args, **kwargs):
|
||||
"""
|
||||
Create a new node
|
||||
|
||||
@@ -249,23 +245,22 @@ class BaseManager:
|
||||
project = ProjectManager.instance().get_project(project_id)
|
||||
if node_id and isinstance(node_id, int):
|
||||
# old project
|
||||
with (yield from BaseManager._convert_lock):
|
||||
node_id = yield from self.convert_old_project(project, node_id, name)
|
||||
async with BaseManager._convert_lock:
|
||||
node_id = await self.convert_old_project(project, node_id, name)
|
||||
|
||||
if not node_id:
|
||||
node_id = str(uuid4())
|
||||
|
||||
node = self._NODE_CLASS(name, node_id, project, self, *args, **kwargs)
|
||||
if asyncio.iscoroutinefunction(node.create):
|
||||
yield from node.create()
|
||||
await node.create()
|
||||
else:
|
||||
node.create()
|
||||
self._nodes[node.id] = node
|
||||
project.add_node(node)
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate_node(self, source_node_id, destination_node_id):
|
||||
async def duplicate_node(self, source_node_id, destination_node_id):
|
||||
"""
|
||||
Duplicate a node
|
||||
|
||||
@@ -296,8 +291,7 @@ class BaseManager:
|
||||
|
||||
return destination_node
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_node(self, node_id):
|
||||
async def close_node(self, node_id):
|
||||
"""
|
||||
Close a node
|
||||
|
||||
@@ -308,13 +302,12 @@ class BaseManager:
|
||||
|
||||
node = self.get_node(node_id)
|
||||
if asyncio.iscoroutinefunction(node.close):
|
||||
yield from node.close()
|
||||
await node.close()
|
||||
else:
|
||||
node.close()
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closing(self, project):
|
||||
async def project_closing(self, project):
|
||||
"""
|
||||
Called when a project is about to be closed.
|
||||
|
||||
@@ -323,8 +316,7 @@ class BaseManager:
|
||||
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
@@ -335,8 +327,7 @@ class BaseManager:
|
||||
if node.id in self._nodes:
|
||||
del self._nodes[node.id]
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete_node(self, node_id):
|
||||
async def delete_node(self, node_id):
|
||||
"""
|
||||
Delete a node. The node working directory will be destroyed when a commit is received.
|
||||
|
||||
@@ -347,11 +338,11 @@ class BaseManager:
|
||||
node = None
|
||||
try:
|
||||
node = self.get_node(node_id)
|
||||
yield from self.close_node(node_id)
|
||||
await self.close_node(node_id)
|
||||
finally:
|
||||
if node:
|
||||
node.project.emit("node.deleted", node)
|
||||
yield from node.project.remove_node(node)
|
||||
await node.project.remove_node(node)
|
||||
if node.id in self._nodes:
|
||||
del self._nodes[node.id]
|
||||
return node
|
||||
@@ -526,8 +517,7 @@ class BaseManager:
|
||||
return relpath
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_images(self):
|
||||
async def list_images(self):
|
||||
"""
|
||||
Return the list of available images for this node type
|
||||
|
||||
@@ -548,8 +538,7 @@ class BaseManager:
|
||||
return default_images_directory(self._NODE_TYPE)
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def write_image(self, filename, stream):
|
||||
async def write_image(self, filename, stream):
|
||||
|
||||
directory = self.get_images_directory()
|
||||
path = os.path.abspath(os.path.join(directory, *os.path.split(filename)))
|
||||
@@ -563,13 +552,13 @@ class BaseManager:
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(tmp_path, 'wb') as f:
|
||||
while True:
|
||||
packet = yield from stream.read(4096)
|
||||
packet = await stream.read(4096)
|
||||
if not packet:
|
||||
break
|
||||
f.write(packet)
|
||||
os.chmod(tmp_path, stat.S_IWRITE | stat.S_IREAD | stat.S_IEXEC)
|
||||
shutil.move(tmp_path, path)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, path)
|
||||
await cancellable_wait_run_in_executor(md5sum, path)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPConflict(text="Could not write image: {} because {}".format(filename, e))
|
||||
|
||||
|
||||
@@ -273,8 +273,7 @@ class BaseNode:
|
||||
name=self.name,
|
||||
id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Delete the node (including all its files).
|
||||
"""
|
||||
@@ -285,7 +284,7 @@ class BaseNode:
|
||||
directory = self.project.node_working_directory(self)
|
||||
if os.path.exists(directory):
|
||||
try:
|
||||
yield from wait_run_in_executor(shutil.rmtree, directory, onerror=set_rw)
|
||||
await wait_run_in_executor(shutil.rmtree, directory, onerror=set_rw)
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the node working directory: {}".format(e))
|
||||
|
||||
@@ -296,13 +295,12 @@ class BaseNode:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stop the node process.
|
||||
"""
|
||||
|
||||
yield from self.stop_wrap_console()
|
||||
await self.stop_wrap_console()
|
||||
self.status = "stopped"
|
||||
|
||||
def suspend(self):
|
||||
@@ -312,8 +310,7 @@ class BaseNode:
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Close the node process.
|
||||
"""
|
||||
@@ -339,8 +336,7 @@ class BaseNode:
|
||||
self._closed = True
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_wrap_console(self):
|
||||
async def start_wrap_console(self):
|
||||
"""
|
||||
Start a telnet proxy for the console allowing multiple client
|
||||
connected at the same time
|
||||
@@ -351,27 +347,26 @@ class BaseNode:
|
||||
remaining_trial = 60
|
||||
while True:
|
||||
try:
|
||||
(reader, writer) = yield from asyncio.open_connection(host="127.0.0.1", port=self._internal_console_port)
|
||||
(reader, writer) = await asyncio.open_connection(host="127.0.0.1", port=self._internal_console_port)
|
||||
break
|
||||
except (OSError, ConnectionRefusedError) as e:
|
||||
if remaining_trial <= 0:
|
||||
raise e
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
remaining_trial -= 1
|
||||
yield from AsyncioTelnetServer.write_client_intro(writer, echo=True)
|
||||
await AsyncioTelnetServer.write_client_intro(writer, echo=True)
|
||||
server = AsyncioTelnetServer(reader=reader, writer=writer, binary=True, echo=True)
|
||||
# warning: this will raise OSError exception if there is a problem...
|
||||
self._wrapper_telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._wrapper_telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_wrap_console(self):
|
||||
async def stop_wrap_console(self):
|
||||
"""
|
||||
Stops the telnet proxy.
|
||||
"""
|
||||
|
||||
if self._wrapper_telnet_server:
|
||||
self._wrapper_telnet_server.close()
|
||||
yield from self._wrapper_telnet_server.wait_closed()
|
||||
await self._wrapper_telnet_server.wait_closed()
|
||||
|
||||
@property
|
||||
def allocate_aux(self):
|
||||
@@ -530,8 +525,7 @@ class BaseNode:
|
||||
path = shutil.which(path)
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_send(self, command):
|
||||
async def _ubridge_send(self, command):
|
||||
"""
|
||||
Sends a command to uBridge hypervisor.
|
||||
|
||||
@@ -539,17 +533,16 @@ class BaseNode:
|
||||
"""
|
||||
|
||||
if not self._ubridge_hypervisor or not self._ubridge_hypervisor.is_running():
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if not self._ubridge_hypervisor or not self._ubridge_hypervisor.is_running():
|
||||
raise NodeError("Cannot send command '{}': uBridge is not running".format(command))
|
||||
try:
|
||||
yield from self._ubridge_hypervisor.send(command)
|
||||
await self._ubridge_hypervisor.send(command)
|
||||
except UbridgeError as e:
|
||||
raise UbridgeError("Error while sending command '{}': {}: {}".format(command, e, self._ubridge_hypervisor.read_stdout()))
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge(self):
|
||||
async def _start_ubridge(self):
|
||||
"""
|
||||
Starts uBridge (handles connections to and from this node).
|
||||
"""
|
||||
@@ -569,24 +562,22 @@ class BaseNode:
|
||||
if not self.ubridge:
|
||||
self._ubridge_hypervisor = Hypervisor(self._project, self.ubridge_path, self.working_dir, server_host)
|
||||
log.info("Starting new uBridge hypervisor {}:{}".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.start()
|
||||
await self._ubridge_hypervisor.start()
|
||||
if self._ubridge_hypervisor:
|
||||
log.info("Hypervisor {}:{} has successfully started".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.connect()
|
||||
await self._ubridge_hypervisor.connect()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge(self):
|
||||
async def _stop_ubridge(self):
|
||||
"""
|
||||
Stops uBridge.
|
||||
"""
|
||||
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
log.info("Stopping uBridge hypervisor {}:{}".format(self._ubridge_hypervisor.host, self._ubridge_hypervisor.port))
|
||||
yield from self._ubridge_hypervisor.stop()
|
||||
await self._ubridge_hypervisor.stop()
|
||||
self._ubridge_hypervisor = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
async def add_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
"""
|
||||
Creates an UDP connection in uBridge.
|
||||
|
||||
@@ -595,43 +586,41 @@ class BaseNode:
|
||||
:param destination_nio: destination NIO instance
|
||||
"""
|
||||
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
|
||||
if not isinstance(destination_nio, NIOUDP):
|
||||
raise NodeError("Destination NIO is not UDP")
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=source_nio.lport,
|
||||
rhost=source_nio.rhost,
|
||||
rport=source_nio.rport))
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=destination_nio.lport,
|
||||
rhost=destination_nio.rhost,
|
||||
rport=destination_nio.rport))
|
||||
|
||||
if destination_nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
pcap_file=destination_nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
await self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
await self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
async def update_ubridge_udp_connection(self, bridge_name, source_nio, destination_nio):
|
||||
if destination_nio:
|
||||
yield from self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, destination_nio.filters)
|
||||
|
||||
def ubridge_delete_bridge(self, name):
|
||||
async def ubridge_delete_bridge(self, name):
|
||||
"""
|
||||
:params name: Delete the bridge with this name
|
||||
"""
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=name))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
async def _ubridge_apply_filters(self, bridge_name, filters):
|
||||
"""
|
||||
Apply packet filters
|
||||
|
||||
@@ -639,11 +628,11 @@ class BaseNode:
|
||||
:param filters: Array of filter dictionary
|
||||
"""
|
||||
|
||||
yield from self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
await self._ubridge_send('bridge reset_packet_filters ' + bridge_name)
|
||||
for packet_filter in self._build_filter_list(filters):
|
||||
cmd = 'bridge add_packet_filter {} {}'.format(bridge_name, packet_filter)
|
||||
try:
|
||||
yield from self._ubridge_send(cmd)
|
||||
await self._ubridge_send(cmd)
|
||||
except UbridgeError as e:
|
||||
match = re.search("Cannot compile filter '(.*)': syntax error", str(e))
|
||||
if match:
|
||||
@@ -675,8 +664,7 @@ class BaseNode:
|
||||
filter_value=" ".join([str(v) for v in values]))
|
||||
i += 1
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=False):
|
||||
async def _add_ubridge_ethernet_connection(self, bridge_name, ethernet_interface, block_host_traffic=False):
|
||||
"""
|
||||
Creates a connection with an Ethernet interface in uBridge.
|
||||
|
||||
@@ -687,7 +675,7 @@ class BaseNode:
|
||||
|
||||
if sys.platform.startswith("linux") and block_host_traffic is False:
|
||||
# on Linux we use RAW sockets by default excepting if host traffic must be blocked
|
||||
yield from self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
await self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
elif sys.platform.startswith("win"):
|
||||
# on Windows we use Winpcap/Npcap
|
||||
windows_interfaces = interfaces()
|
||||
@@ -702,26 +690,26 @@ class BaseNode:
|
||||
npf_id = interface["id"]
|
||||
source_mac = interface["mac_address"]
|
||||
if npf_id:
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name,
|
||||
interface=npf_id))
|
||||
else:
|
||||
raise NodeError("Could not find NPF id for interface {}".format(ethernet_interface))
|
||||
|
||||
if block_host_traffic:
|
||||
if source_mac:
|
||||
yield from self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
await self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
log.info('PCAP filter applied on "{interface}" for source MAC {mac}'.format(interface=ethernet_interface, mac=source_mac))
|
||||
else:
|
||||
log.warning("Could not block host network traffic on {} (no MAC address found)".format(ethernet_interface))
|
||||
else:
|
||||
# on other platforms we just rely on the pcap library
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=ethernet_interface))
|
||||
source_mac = None
|
||||
for interface in interfaces():
|
||||
if interface["name"] == ethernet_interface:
|
||||
source_mac = interface["mac_address"]
|
||||
if source_mac:
|
||||
yield from self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
await self._ubridge_send('bridge set_pcap_filter {name} "not ether src {mac}"'.format(name=bridge_name, mac=source_mac))
|
||||
log.info('PCAP filter applied on "{interface}" for source MAC {mac}'.format(interface=ethernet_interface, mac=source_mac))
|
||||
|
||||
def _create_local_udp_tunnel(self):
|
||||
|
||||
@@ -207,58 +207,54 @@ class Cloud(BaseNode):
|
||||
|
||||
self._ports_mapping = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this cloud.
|
||||
"""
|
||||
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
log.info('Cloud "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this cloud.
|
||||
"""
|
||||
|
||||
if self.status != "started":
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._stop_ubridge()
|
||||
yield from self._start_ubridge()
|
||||
await self._stop_ubridge()
|
||||
await self._start_ubridge()
|
||||
for port_number in self._nios:
|
||||
if self._nios[port_number]:
|
||||
try:
|
||||
yield from self._add_ubridge_connection(self._nios[port_number], port_number)
|
||||
await self._add_ubridge_connection(self._nios[port_number], port_number)
|
||||
except (UbridgeError, NodeError) as e:
|
||||
self.status = "stopped"
|
||||
raise e
|
||||
self.status = "started"
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this cloud.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
for nio in self._nios.values():
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
log.info('Cloud "{name}" [{id}] has been closed'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _is_wifi_adapter_osx(self, adapter_name):
|
||||
async def _is_wifi_adapter_osx(self, adapter_name):
|
||||
"""
|
||||
Detects a Wifi adapter on Mac.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports")
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output("networksetup", "-listallhardwareports")
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("Could not execute networksetup: {}".format(e))
|
||||
return False
|
||||
@@ -274,8 +270,7 @@ class Cloud(BaseNode):
|
||||
is_wifi = True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, port_number):
|
||||
async def _add_ubridge_connection(self, nio, port_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@@ -294,19 +289,19 @@ class Cloud(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge create {name}".format(name=bridge_name))
|
||||
if not isinstance(nio, NIOUDP):
|
||||
raise NodeError("Source NIO is not UDP")
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
if port_info["type"] in ("ethernet", "tap"):
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
yield from self._add_ubridge_ethernet_connection(bridge_name, port_info["interface"])
|
||||
await self._add_ubridge_ethernet_connection(bridge_name, port_info["interface"])
|
||||
|
||||
else:
|
||||
|
||||
@@ -316,29 +311,28 @@ class Cloud(BaseNode):
|
||||
raise NodeError("Interface '{}' could not be found on this system".format(port_info["interface"]))
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
yield from self._add_linux_ethernet(port_info, bridge_name)
|
||||
await self._add_linux_ethernet(port_info, bridge_name)
|
||||
elif sys.platform.startswith("darwin"):
|
||||
yield from self._add_osx_ethernet(port_info, bridge_name)
|
||||
await self._add_osx_ethernet(port_info, bridge_name)
|
||||
else:
|
||||
yield from self._add_windows_ethernet(port_info, bridge_name)
|
||||
await self._add_windows_ethernet(port_info, bridge_name)
|
||||
|
||||
elif port_info["type"] == "tap":
|
||||
yield from self._ubridge_send('bridge add_nio_tap {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_tap {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
elif port_info["type"] == "udp":
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=bridge_name,
|
||||
lport=port_info["lport"],
|
||||
rhost=port_info["rhost"],
|
||||
rport=port_info["rport"]))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=bridge_name,
|
||||
pcap_file=nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
await self._ubridge_send('bridge start {name}'.format(name=bridge_name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_linux_ethernet(self, port_info, bridge_name):
|
||||
async def _add_linux_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on Linux using raw sockets.
|
||||
|
||||
@@ -356,41 +350,38 @@ class Cloud(BaseNode):
|
||||
break
|
||||
i += 1
|
||||
|
||||
yield from self._ubridge_send('bridge add_nio_tap "{name}" "{interface}"'.format(name=bridge_name, interface=tap))
|
||||
yield from self._ubridge_send('brctl addif "{interface}" "{tap}"'.format(tap=tap, interface=interface))
|
||||
await self._ubridge_send('bridge add_nio_tap "{name}" "{interface}"'.format(name=bridge_name, interface=tap))
|
||||
await self._ubridge_send('brctl addif "{interface}" "{tap}"'.format(tap=tap, interface=interface))
|
||||
else:
|
||||
yield from self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=interface))
|
||||
await self._ubridge_send('bridge add_nio_linux_raw {name} "{interface}"'.format(name=bridge_name, interface=interface))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_osx_ethernet(self, port_info, bridge_name):
|
||||
async def _add_osx_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on OSX using libpcap.
|
||||
"""
|
||||
|
||||
# Wireless adapters are not well supported by the libpcap on OSX
|
||||
if (yield from self._is_wifi_adapter_osx(port_info["interface"])):
|
||||
if (await self._is_wifi_adapter_osx(port_info["interface"])):
|
||||
raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
|
||||
if port_info["interface"].startswith("vmnet"):
|
||||
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them)
|
||||
yield from self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
|
||||
interface=port_info["interface"]))
|
||||
return
|
||||
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
|
||||
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_windows_ethernet(self, port_info, bridge_name):
|
||||
async def _add_windows_ethernet(self, port_info, bridge_name):
|
||||
"""
|
||||
Connects an Ethernet interface on Windows.
|
||||
"""
|
||||
|
||||
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
|
||||
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))
|
||||
yield from self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
await self._ubridge_send('bridge add_nio_ethernet {name} "{interface}"'.format(name=bridge_name, interface=port_info["interface"]))
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this cloud.
|
||||
|
||||
@@ -406,23 +397,22 @@ class Cloud(BaseNode):
|
||||
nio=nio,
|
||||
port=port_number))
|
||||
try:
|
||||
yield from self.start()
|
||||
yield from self._add_ubridge_connection(nio, port_number)
|
||||
await self.start()
|
||||
await self._add_ubridge_connection(nio, port_number)
|
||||
self._nios[port_number] = nio
|
||||
except NodeError as e:
|
||||
self.project.emit("log.error", {"message": str(e)})
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
self.status = "stopped"
|
||||
self._nios[port_number] = nio
|
||||
# Cleanup stuff
|
||||
except UbridgeError as e:
|
||||
self.project.emit("log.error", {"message": str(e)})
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
self.status = "stopped"
|
||||
self._nios[port_number] = nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_nio(self, port_number, nio):
|
||||
async def update_nio(self, port_number, nio):
|
||||
"""
|
||||
Update an nio on this node
|
||||
|
||||
@@ -432,10 +422,9 @@ class Cloud(BaseNode):
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _delete_ubridge_connection(self, port_number):
|
||||
async def _delete_ubridge_connection(self, port_number):
|
||||
"""
|
||||
Deletes a connection in uBridge.
|
||||
|
||||
@@ -443,10 +432,9 @@ class Cloud(BaseNode):
|
||||
"""
|
||||
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=bridge_name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of cloud.
|
||||
|
||||
@@ -469,12 +457,11 @@ class Cloud(BaseNode):
|
||||
|
||||
del self._nios[port_number]
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
yield from self._delete_ubridge_connection(port_number)
|
||||
yield from self.start()
|
||||
await self._delete_ubridge_connection(port_number)
|
||||
await self.start()
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -496,14 +483,13 @@ class Cloud(BaseNode):
|
||||
raise NodeError("Packet capture is already activated on port {port_number}".format(port_number=port_number))
|
||||
nio.startPacketCapture(output_file)
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=bridge_name,
|
||||
output_file=output_file))
|
||||
log.info("Cloud '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -520,7 +506,7 @@ class Cloud(BaseNode):
|
||||
nio = self._nios[port_number]
|
||||
nio.stopPacketCapture()
|
||||
bridge_name = "{}-{}".format(self._id, port_number)
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=bridge_name))
|
||||
|
||||
log.info("Cloud'{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
||||
@@ -44,8 +44,7 @@ class EthernetHub(BaseNode):
|
||||
"node_id": self.id,
|
||||
"project_id": self.project.id}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this hub.
|
||||
"""
|
||||
@@ -53,16 +52,14 @@ class EthernetHub(BaseNode):
|
||||
super().create()
|
||||
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this hub.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this hub.
|
||||
|
||||
@@ -72,8 +69,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this hub.
|
||||
|
||||
@@ -84,8 +80,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -96,8 +91,7 @@ class EthernetHub(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
|
||||
@@ -44,8 +44,7 @@ class EthernetSwitch(BaseNode):
|
||||
"node_id": self.id,
|
||||
"project_id": self.project.id}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this switch.
|
||||
"""
|
||||
@@ -53,16 +52,14 @@ class EthernetSwitch(BaseNode):
|
||||
super().create()
|
||||
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this switch.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this switch.
|
||||
|
||||
@@ -72,8 +69,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this switch.
|
||||
|
||||
@@ -84,8 +80,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -96,8 +91,7 @@ class EthernetSwitch(BaseNode):
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
|
||||
@@ -54,14 +54,13 @@ class Docker(BaseManager):
|
||||
self._session = None
|
||||
self._api_version = DOCKER_MINIMUM_API_VERSION
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_connection(self):
|
||||
async def _check_connection(self):
|
||||
|
||||
if not self._connected:
|
||||
try:
|
||||
self._connected = True
|
||||
connector = self.connector()
|
||||
version = yield from self.query("GET", "version")
|
||||
version = await self.query("GET", "version")
|
||||
except (aiohttp.ClientOSError, FileNotFoundError):
|
||||
self._connected = False
|
||||
raise DockerError("Can't connect to docker daemon")
|
||||
@@ -88,16 +87,14 @@ class Docker(BaseManager):
|
||||
raise DockerError("Can't connect to docker daemon")
|
||||
return self._connector
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
yield from super().unload()
|
||||
await super().unload()
|
||||
if self._connected:
|
||||
if self._connector and not self._connector.closed:
|
||||
self._connector.close()
|
||||
|
||||
@asyncio.coroutine
|
||||
def query(self, method, path, data={}, params={}):
|
||||
async def query(self, method, path, data={}, params={}):
|
||||
"""
|
||||
Makes a query to the Docker daemon and decode the request
|
||||
|
||||
@@ -107,8 +104,8 @@ class Docker(BaseManager):
|
||||
:param params: Parameters added as a query arg
|
||||
"""
|
||||
|
||||
response = yield from self.http_query(method, path, data=data, params=params)
|
||||
body = yield from response.read()
|
||||
response = await self.http_query(method, path, data=data, params=params)
|
||||
body = await response.read()
|
||||
if body and len(body):
|
||||
if response.headers['CONTENT-TYPE'] == 'application/json':
|
||||
body = json.loads(body.decode("utf-8"))
|
||||
@@ -117,8 +114,7 @@ class Docker(BaseManager):
|
||||
log.debug("Query Docker %s %s params=%s data=%s Response: %s", method, path, params, data, body)
|
||||
return body
|
||||
|
||||
@asyncio.coroutine
|
||||
def http_query(self, method, path, data={}, params={}, timeout=300):
|
||||
async def http_query(self, method, path, data={}, params={}, timeout=300):
|
||||
"""
|
||||
Makes a query to the docker daemon
|
||||
|
||||
@@ -140,11 +136,11 @@ class Docker(BaseManager):
|
||||
url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path
|
||||
try:
|
||||
if path != "version": # version is use by check connection
|
||||
yield from self._check_connection()
|
||||
await self._check_connection()
|
||||
if self._session is None or self._session.closed:
|
||||
connector = self.connector()
|
||||
self._session = aiohttp.ClientSession(connector=connector)
|
||||
response = yield from self._session.request(
|
||||
response = await self._session.request(
|
||||
method,
|
||||
url,
|
||||
params=params,
|
||||
@@ -157,7 +153,7 @@ class Docker(BaseManager):
|
||||
except (asyncio.TimeoutError):
|
||||
raise DockerError("Docker timeout " + method + " " + path)
|
||||
if response.status >= 300:
|
||||
body = yield from response.read()
|
||||
body = await response.read()
|
||||
try:
|
||||
body = json.loads(body.decode("utf-8"))["message"]
|
||||
except ValueError:
|
||||
@@ -171,8 +167,7 @@ class Docker(BaseManager):
|
||||
raise DockerError("Docker has returned an error: {} {}".format(response.status, body))
|
||||
return response
|
||||
|
||||
@asyncio.coroutine
|
||||
def websocket_query(self, path, params={}):
|
||||
async def websocket_query(self, path, params={}):
|
||||
"""
|
||||
Opens a websocket connection
|
||||
|
||||
@@ -182,14 +177,13 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
url = "http://docker/v" + self._api_version + "/" + path
|
||||
connection = yield from self._session.ws_connect(url,
|
||||
connection = await self._session.ws_connect(url,
|
||||
origin="http://docker",
|
||||
autoping=True)
|
||||
return connection
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def pull_image(self, image, progress_callback=None):
|
||||
async def pull_image(self, image, progress_callback=None):
|
||||
"""
|
||||
Pulls an image from the Docker repository
|
||||
|
||||
@@ -198,19 +192,19 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self.query("GET", "images/{}/json".format(image))
|
||||
await self.query("GET", "images/{}/json".format(image))
|
||||
return # We already have the image skip the download
|
||||
except DockerHttp404Error:
|
||||
pass
|
||||
|
||||
if progress_callback:
|
||||
progress_callback("Pulling '{}' from docker hub".format(image))
|
||||
response = yield from self.http_query("POST", "images/create", params={"fromImage": image}, timeout=None)
|
||||
response = await self.http_query("POST", "images/create", params={"fromImage": image}, timeout=None)
|
||||
# The pull api will stream status via an HTTP JSON stream
|
||||
content = ""
|
||||
while True:
|
||||
try:
|
||||
chunk = yield from response.content.read(1024)
|
||||
chunk = await response.content.read(1024)
|
||||
except aiohttp.ServerDisconnectedError:
|
||||
log.error("Disconnected from server while pulling Docker image '{}' from docker hub".format(image))
|
||||
break
|
||||
@@ -234,8 +228,7 @@ class Docker(BaseManager):
|
||||
if progress_callback:
|
||||
progress_callback("Success pulling image {}".format(image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_images(self):
|
||||
async def list_images(self):
|
||||
"""
|
||||
Gets Docker image list.
|
||||
|
||||
@@ -244,7 +237,7 @@ class Docker(BaseManager):
|
||||
"""
|
||||
|
||||
images = []
|
||||
for image in (yield from self.query("GET", "images/json", params={"all": 0})):
|
||||
for image in (await self.query("GET", "images/json", params={"all": 0})):
|
||||
if image['RepoTags']:
|
||||
for tag in image['RepoTags']:
|
||||
if tag != "<none>:<none>":
|
||||
|
||||
@@ -29,7 +29,6 @@ import os
|
||||
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
|
||||
from gns3server.utils.asyncio.raw_command_server import AsyncioRawCommandServer
|
||||
from gns3server.utils.asyncio import wait_for_file_creation
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from gns3server.utils.asyncio import monitor_process
|
||||
from gns3server.utils.get_resource import get_resource
|
||||
|
||||
@@ -195,8 +194,7 @@ class DockerVM(BaseNode):
|
||||
def extra_hosts(self, extra_hosts):
|
||||
self._extra_hosts = extra_hosts
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_container_state(self):
|
||||
async def _get_container_state(self):
|
||||
"""
|
||||
Returns the container state (e.g. running, paused etc.)
|
||||
|
||||
@@ -205,7 +203,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
try:
|
||||
result = yield from self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
result = await self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
except DockerError:
|
||||
return "exited"
|
||||
|
||||
@@ -215,13 +213,12 @@ class DockerVM(BaseNode):
|
||||
return "running"
|
||||
return "exited"
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_image_information(self):
|
||||
async def _get_image_information(self):
|
||||
"""
|
||||
:returns: Dictionary information about the container image
|
||||
"""
|
||||
|
||||
result = yield from self.manager.query("GET", "images/{}/json".format(self._image))
|
||||
result = await self.manager.query("GET", "images/{}/json".format(self._image))
|
||||
return result
|
||||
|
||||
def _mount_binds(self, image_info):
|
||||
@@ -288,18 +285,17 @@ class DockerVM(BaseNode):
|
||||
# iface eth{adapter} inet dhcp""".format(adapter=adapter))
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates the Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
image_infos = yield from self._get_image_information()
|
||||
image_infos = await self._get_image_information()
|
||||
except DockerHttp404Error:
|
||||
log.info("Image '{}' is missing, pulling it from Docker hub...".format(self._image))
|
||||
yield from self.pull_image(self._image)
|
||||
image_infos = yield from self._get_image_information()
|
||||
await self.pull_image(self._image)
|
||||
image_infos = await self._get_image_information()
|
||||
|
||||
if image_infos is None:
|
||||
raise DockerError("Cannot get information for image '{}', please try again.".format(self._image))
|
||||
@@ -359,7 +355,7 @@ class DockerVM(BaseNode):
|
||||
params["Env"].append(formatted)
|
||||
|
||||
if self._console_type == "vnc":
|
||||
yield from self._start_vnc()
|
||||
await self._start_vnc()
|
||||
params["Env"].append("QT_GRAPHICSSYSTEM=native") # To fix a Qt issue: https://github.com/GNS3/gns3-server/issues/556
|
||||
params["Env"].append("DISPLAY=:{}".format(self._display))
|
||||
params["HostConfig"]["Binds"].append("/tmp/.X11-unix/:/tmp/.X11-unix/")
|
||||
@@ -369,7 +365,7 @@ class DockerVM(BaseNode):
|
||||
if extra_hosts:
|
||||
params["Env"].append("GNS3_EXTRA_HOSTS={}".format(extra_hosts))
|
||||
|
||||
result = yield from self.manager.query("POST", "containers/create", data=params)
|
||||
result = await self.manager.query("POST", "containers/create", data=params)
|
||||
self._cid = result['Id']
|
||||
log.info("Docker container '{name}' [{id}] created".format(name=self._name, id=self._id))
|
||||
return True
|
||||
@@ -393,8 +389,7 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Can't apply `ExtraHosts`, wrong format: {}".format(extra_hosts))
|
||||
return "\n".join(["{}\t{}".format(h[1], h[0]) for h in hosts])
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
"""
|
||||
Destroy an recreate the container with the new settings
|
||||
"""
|
||||
@@ -402,66 +397,65 @@ class DockerVM(BaseNode):
|
||||
# We need to save the console and state and restore it
|
||||
console = self.console
|
||||
aux = self.aux
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
|
||||
# reset the docker container, but don't release the NIO UDP ports
|
||||
yield from self.reset(False)
|
||||
yield from self.create()
|
||||
await self.reset(False)
|
||||
await self.create()
|
||||
self.console = console
|
||||
self.aux = aux
|
||||
if state == "running":
|
||||
yield from self.start()
|
||||
await self.start()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
except DockerHttp404Error:
|
||||
raise DockerError("Docker container '{name}' with ID {cid} does not exist or is not ready yet. Please try again in a few seconds.".format(name=self.name,
|
||||
cid=self._cid))
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
await self.unpause()
|
||||
elif state == "running":
|
||||
return
|
||||
else:
|
||||
|
||||
if self._console_type == "vnc" and not self._x11vnc_process:
|
||||
# start the x11vnc process in case it had previously crashed
|
||||
self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
self._x11vnc_process = await asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
|
||||
yield from self._clean_servers()
|
||||
await self._clean_servers()
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
self._namespace = yield from self._get_namespace()
|
||||
await self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
self._namespace = await self._get_namespace()
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
|
||||
for adapter_number in range(0, self.adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
with (yield from self.manager.ubridge_lock):
|
||||
async with self.manager.ubridge_lock:
|
||||
try:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
except UbridgeNamespaceError:
|
||||
log.error("Container %s failed to start", self.name)
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
# The container can crash soon after the start, this means we can not move the interface to the container namespace
|
||||
logdata = yield from self._get_log()
|
||||
logdata = await self._get_log()
|
||||
for line in logdata.split('\n'):
|
||||
log.error(line)
|
||||
raise DockerError(logdata)
|
||||
|
||||
if self.console_type == "telnet":
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
elif self.console_type == "http" or self.console_type == "https":
|
||||
yield from self._start_http()
|
||||
await self._start_http()
|
||||
|
||||
if self.allocate_aux:
|
||||
yield from self._start_aux()
|
||||
await self._start_aux()
|
||||
|
||||
self.status = "started"
|
||||
log.info("Docker container '{name}' [{image}] started listen for {console_type} on {console}".format(name=self._name,
|
||||
@@ -469,8 +463,7 @@ class DockerVM(BaseNode):
|
||||
console=self.console,
|
||||
console_type=self.console_type))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_aux(self):
|
||||
async def _start_aux(self):
|
||||
"""
|
||||
Start an auxiliary console
|
||||
"""
|
||||
@@ -478,7 +471,7 @@ class DockerVM(BaseNode):
|
||||
# We can not use the API because docker doesn't expose a websocket api for exec
|
||||
# https://github.com/GNS3/gns3-gui/issues/1039
|
||||
try:
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do TERM=vt100 /gns3/bin/busybox sh; done", "/dev/null",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
@@ -487,29 +480,28 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Could not start auxiliary console process: {}".format(e))
|
||||
server = AsyncioTelnetServer(reader=process.stdout, writer=process.stdin, binary=True, echo=True)
|
||||
try:
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
|
||||
self._telnet_servers.append((await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.aux)))
|
||||
except OSError as e:
|
||||
raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.aux, e))
|
||||
log.debug("Docker container '%s' started listen for auxilary telnet on %d", self.name, self.aux)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _fix_permissions(self):
|
||||
async def _fix_permissions(self):
|
||||
"""
|
||||
Because docker run as root we need to fix permission and ownership to allow user to interact
|
||||
with it from their filesystem and do operation like file delete
|
||||
"""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "stopped" or state == "exited":
|
||||
# We need to restart it to fix permissions
|
||||
yield from self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/start".format(self._cid))
|
||||
|
||||
for volume in self._volumes:
|
||||
log.debug("Docker container '{name}' [{image}] fix ownership on {path}".format(
|
||||
name=self._name, image=self._image, path=volume))
|
||||
|
||||
try:
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker",
|
||||
"exec",
|
||||
self._cid,
|
||||
@@ -526,10 +518,9 @@ class DockerVM(BaseNode):
|
||||
)
|
||||
except OSError as e:
|
||||
raise DockerError("Could not fix permissions for {}: {}".format(volume, e))
|
||||
yield from process.wait()
|
||||
await process.wait()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_vnc(self):
|
||||
async def _start_vnc(self):
|
||||
"""
|
||||
Starts a VNC server for this container
|
||||
"""
|
||||
@@ -537,11 +528,11 @@ class DockerVM(BaseNode):
|
||||
self._display = self._get_free_display_port()
|
||||
if shutil.which("Xvfb") is None or shutil.which("x11vnc") is None:
|
||||
raise DockerError("Please install Xvfb and x11vnc before using VNC support")
|
||||
self._xvfb_process = yield from asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16")
|
||||
self._xvfb_process = await asyncio.create_subprocess_exec("Xvfb", "-nolisten", "tcp", ":{}".format(self._display), "-screen", "0", self._console_resolution + "x16")
|
||||
# We pass a port for TCPV6 due to a crash in X11VNC if not here: https://github.com/GNS3/gns3-server/issues/569
|
||||
self._x11vnc_process = yield from asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
self._x11vnc_process = await asyncio.create_subprocess_exec("x11vnc", "-forever", "-nopw", "-shared", "-geometry", self._console_resolution, "-display", "WAIT:{}".format(self._display), "-rfbport", str(self.console), "-rfbportv6", str(self.console), "-noncache", "-listen", self._manager.port_manager.console_host)
|
||||
x11_socket = os.path.join("/tmp/.X11-unix/", "X{}".format(self._display))
|
||||
yield from wait_for_file_creation(x11_socket)
|
||||
await wait_for_file_creation(x11_socket)
|
||||
|
||||
# sometimes the x11vnc process can crash
|
||||
monitor_process(self._x11vnc_process, self._x11vnc_callback)
|
||||
@@ -557,8 +548,7 @@ class DockerVM(BaseNode):
|
||||
self.project.emit("log.error", {"message": "The x11vnc process has stopped with return code {} for node '{}'. Please restart this node.".format(returncode, self.name)})
|
||||
self._x11vnc_process = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_http(self):
|
||||
async def _start_http(self):
|
||||
"""
|
||||
Starts an HTTP tunnel to container localhost. It's not perfect
|
||||
but the only way we have to inject network packet is using nc.
|
||||
@@ -577,10 +567,9 @@ class DockerVM(BaseNode):
|
||||
':{}'.format(self.console).encode(),
|
||||
)
|
||||
])
|
||||
self._telnet_servers.append((yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)))
|
||||
self._telnet_servers.append((await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts streaming the console via telnet
|
||||
"""
|
||||
@@ -593,8 +582,7 @@ class DockerVM(BaseNode):
|
||||
def write(self, data):
|
||||
self._data += data
|
||||
|
||||
@asyncio.coroutine
|
||||
def drain(self):
|
||||
async def drain(self):
|
||||
if not self.ws.closed:
|
||||
self.ws.send_bytes(self._data)
|
||||
self._data = b""
|
||||
@@ -604,19 +592,18 @@ class DockerVM(BaseNode):
|
||||
|
||||
telnet = AsyncioTelnetServer(reader=output_stream, writer=input_stream, echo=True)
|
||||
try:
|
||||
self._telnet_servers.append((yield from asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console)))
|
||||
self._telnet_servers.append((await asyncio.start_server(telnet.run, self._manager.port_manager.console_host, self.console)))
|
||||
except OSError as e:
|
||||
raise DockerError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e))
|
||||
|
||||
self._console_websocket = yield from self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid))
|
||||
self._console_websocket = await self.manager.websocket_query("containers/{}/attach/ws?stream=1&stdin=1&stdout=1&stderr=1".format(self._cid))
|
||||
input_stream.ws = self._console_websocket
|
||||
|
||||
output_stream.feed_data(self.name.encode() + b" console is now available... Press RETURN to get started.\r\n")
|
||||
|
||||
asyncio_ensure_future(self._read_console_output(self._console_websocket, output_stream))
|
||||
asyncio.ensure_future(self._read_console_output(self._console_websocket, output_stream))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _read_console_output(self, ws, out):
|
||||
async def _read_console_output(self, ws, out):
|
||||
"""
|
||||
Reads Websocket and forward it to the telnet
|
||||
|
||||
@@ -625,7 +612,7 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
while True:
|
||||
msg = yield from ws.receive()
|
||||
msg = await ws.receive()
|
||||
if msg.tp == aiohttp.WSMsgType.text:
|
||||
out.feed_data(msg.data.encode())
|
||||
elif msg.tp == aiohttp.WSMsgType.BINARY:
|
||||
@@ -636,10 +623,9 @@ class DockerVM(BaseNode):
|
||||
out.feed_eof()
|
||||
ws.close()
|
||||
break
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def is_running(self):
|
||||
async def is_running(self):
|
||||
"""
|
||||
Checks if the container is running.
|
||||
|
||||
@@ -647,25 +633,23 @@ class DockerVM(BaseNode):
|
||||
:rtype: bool
|
||||
"""
|
||||
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "running":
|
||||
return True
|
||||
if self.status == "started": # The container crashed we need to clean
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def restart(self):
|
||||
async def restart(self):
|
||||
"""
|
||||
Restart this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/restart".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/restart".format(self._cid))
|
||||
log.info("Docker container '{name}' [{image}] restarted".format(
|
||||
name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _clean_servers(self):
|
||||
async def _clean_servers(self):
|
||||
"""
|
||||
Clean the list of running console servers
|
||||
"""
|
||||
@@ -673,34 +657,33 @@ class DockerVM(BaseNode):
|
||||
if len(self._telnet_servers) > 0:
|
||||
for telnet_server in self._telnet_servers:
|
||||
telnet_server.close()
|
||||
yield from telnet_server.wait_closed()
|
||||
await telnet_server.wait_closed()
|
||||
self._telnet_servers = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this Docker container.
|
||||
"""
|
||||
|
||||
try:
|
||||
yield from self._clean_servers()
|
||||
yield from self._stop_ubridge()
|
||||
await self._clean_servers()
|
||||
await self._stop_ubridge()
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
except DockerHttp404Error:
|
||||
self.status = "stopped"
|
||||
return
|
||||
|
||||
if state == "paused":
|
||||
yield from self.unpause()
|
||||
await self.unpause()
|
||||
|
||||
yield from self._fix_permissions()
|
||||
state = yield from self._get_container_state()
|
||||
await self._fix_permissions()
|
||||
state = await self._get_container_state()
|
||||
if state != "stopped" or state != "exited":
|
||||
# t=5 number of seconds to wait before killing the container
|
||||
try:
|
||||
yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
|
||||
await self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
|
||||
log.info("Docker container '{name}' [{image}] stopped".format(
|
||||
name=self._name, image=self._image))
|
||||
except DockerHttp304Error:
|
||||
@@ -712,56 +695,52 @@ class DockerVM(BaseNode):
|
||||
return
|
||||
self.status = "stopped"
|
||||
|
||||
@asyncio.coroutine
|
||||
def pause(self):
|
||||
async def pause(self):
|
||||
"""
|
||||
Pauses this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/pause".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/pause".format(self._cid))
|
||||
self.status = "suspended"
|
||||
log.info("Docker container '{name}' [{image}] paused".format(name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def unpause(self):
|
||||
async def unpause(self):
|
||||
"""
|
||||
Unpauses this Docker container.
|
||||
"""
|
||||
|
||||
yield from self.manager.query("POST", "containers/{}/unpause".format(self._cid))
|
||||
await self.manager.query("POST", "containers/{}/unpause".format(self._cid))
|
||||
self.status = "started"
|
||||
log.info("Docker container '{name}' [{image}] unpaused".format(name=self._name, image=self._image))
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this Docker container.
|
||||
"""
|
||||
|
||||
self._closing = True
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
yield from self.reset()
|
||||
await self.reset()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset(self, release_nio_udp_ports=True):
|
||||
async def reset(self, release_nio_udp_ports=True):
|
||||
|
||||
try:
|
||||
state = yield from self._get_container_state()
|
||||
state = await self._get_container_state()
|
||||
if state == "paused" or state == "running":
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
if self.console_type == "vnc":
|
||||
if self._x11vnc_process:
|
||||
try:
|
||||
self._x11vnc_process.terminate()
|
||||
yield from self._x11vnc_process.wait()
|
||||
await self._x11vnc_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
if self._xvfb_process:
|
||||
try:
|
||||
self._xvfb_process.terminate()
|
||||
yield from self._xvfb_process.wait()
|
||||
await self._xvfb_process.wait()
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
|
||||
@@ -776,7 +755,7 @@ class DockerVM(BaseNode):
|
||||
# v – 1/True/true or 0/False/false, Remove the volumes associated to the container. Default false.
|
||||
# force - 1/True/true or 0/False/false, Kill then remove the container. Default false.
|
||||
try:
|
||||
yield from self.manager.query("DELETE", "containers/{}".format(self._cid), params={"force": 1, "v": 1})
|
||||
await self.manager.query("DELETE", "containers/{}".format(self._cid), params={"force": 1, "v": 1})
|
||||
except DockerError:
|
||||
pass
|
||||
log.info("Docker container '{name}' [{image}] removed".format(
|
||||
@@ -793,8 +772,7 @@ class DockerVM(BaseNode):
|
||||
log.debug("Docker error when closing: {}".format(str(e)))
|
||||
return
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, adapter_number):
|
||||
async def _add_ubridge_connection(self, nio, adapter_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@@ -816,44 +794,41 @@ class DockerVM(BaseNode):
|
||||
raise DockerError("Adapter {adapter_number} couldn't allocate interface on Docker container '{name}'. Too many Docker interfaces already exists".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send('bridge create {}'.format(bridge_name))
|
||||
await self._ubridge_send('bridge create {}'.format(bridge_name))
|
||||
self._bridges.add(bridge_name)
|
||||
yield from self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(adapter_number=adapter_number,
|
||||
await self._ubridge_send('bridge add_nio_tap bridge{adapter_number} {hostif}'.format(adapter_number=adapter_number,
|
||||
hostif=adapter.host_ifc))
|
||||
log.debug("Move container %s adapter %s to namespace %s", self.name, adapter.host_ifc, self._namespace)
|
||||
try:
|
||||
yield from self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(ifc=adapter.host_ifc,
|
||||
await self._ubridge_send('docker move_to_ns {ifc} {ns} eth{adapter}'.format(ifc=adapter.host_ifc,
|
||||
ns=self._namespace,
|
||||
adapter=adapter_number))
|
||||
except UbridgeError as e:
|
||||
raise UbridgeNamespaceError(e)
|
||||
|
||||
if nio:
|
||||
yield from self._connect_nio(adapter_number, nio)
|
||||
await self._connect_nio(adapter_number, nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_namespace(self):
|
||||
async def _get_namespace(self):
|
||||
|
||||
result = yield from self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
result = await self.manager.query("GET", "containers/{}/json".format(self._cid))
|
||||
return int(result['State']['Pid'])
|
||||
|
||||
@asyncio.coroutine
|
||||
def _connect_nio(self, adapter_number, nio):
|
||||
async def _connect_nio(self, adapter_number, nio):
|
||||
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send('bridge add_nio_udp {bridge_name} {lport} {rhost} {rport}'.format(bridge_name=bridge_name,
|
||||
await self._ubridge_send('bridge add_nio_udp {bridge_name} {lport} {rhost} {rport}'.format(bridge_name=bridge_name,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {bridge_name} "{pcap_file}"'.format(bridge_name=bridge_name,
|
||||
await self._ubridge_send('bridge start_capture {bridge_name} "{pcap_file}"'.format(bridge_name=bridge_name,
|
||||
pcap_file=nio.pcap_output_file))
|
||||
yield from self._ubridge_send('bridge start {bridge_name}'.format(bridge_name=bridge_name))
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_send('bridge start {bridge_name}'.format(bridge_name=bridge_name))
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@@ -868,7 +843,7 @@ class DockerVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._connect_nio(adapter_number, nio)
|
||||
await self._connect_nio(adapter_number, nio)
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("Docker container '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
@@ -876,8 +851,7 @@ class DockerVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@@ -888,10 +862,9 @@ class DockerVM(BaseNode):
|
||||
if self.ubridge:
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
if bridge_name in self._bridges:
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@@ -909,8 +882,8 @@ class DockerVM(BaseNode):
|
||||
if self.ubridge:
|
||||
nio = adapter.get_nio(0)
|
||||
bridge_name = 'bridge{}'.format(adapter_number)
|
||||
yield from self._ubridge_send("bridge stop {}".format(bridge_name))
|
||||
yield from self._ubridge_send('bridge remove_nio_udp bridge{adapter} {lport} {rhost} {rport}'.format(adapter=adapter_number,
|
||||
await self._ubridge_send("bridge stop {}".format(bridge_name))
|
||||
await self._ubridge_send('bridge remove_nio_udp bridge{adapter} {lport} {rhost} {rport}'.format(adapter=adapter_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
@@ -952,18 +925,16 @@ class DockerVM(BaseNode):
|
||||
id=self._id,
|
||||
adapters=adapters))
|
||||
|
||||
@asyncio.coroutine
|
||||
def pull_image(self, image):
|
||||
async def pull_image(self, image):
|
||||
"""
|
||||
Pulls an image from Docker repository
|
||||
"""
|
||||
|
||||
def callback(msg):
|
||||
self.project.emit("log.info", {"message": msg})
|
||||
yield from self.manager.pull_image(image, progress_callback=callback)
|
||||
await self.manager.pull_image(image, progress_callback=callback)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
async def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture in uBridge.
|
||||
|
||||
@@ -974,10 +945,9 @@ class DockerVM(BaseNode):
|
||||
adapter = "bridge{}".format(adapter_number)
|
||||
if not self.ubridge:
|
||||
raise DockerError("Cannot start the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=adapter, output_file=output_file))
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=adapter, output_file=output_file))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge_capture(self, adapter_number):
|
||||
async def _stop_ubridge_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture in uBridge.
|
||||
|
||||
@@ -987,10 +957,9 @@ class DockerVM(BaseNode):
|
||||
adapter = "bridge{}".format(adapter_number)
|
||||
if not self.ubridge:
|
||||
raise DockerError("Cannot stop the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=adapter))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=adapter))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -1015,13 +984,13 @@ class DockerVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._start_ubridge_capture(adapter_number, output_file)
|
||||
await self._start_ubridge_capture(adapter_number, output_file)
|
||||
|
||||
log.info("Docker VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -1042,28 +1011,26 @@ class DockerVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.status == "started" and self.ubridge:
|
||||
yield from self._stop_ubridge_capture(adapter_number)
|
||||
await self._stop_ubridge_capture(adapter_number)
|
||||
|
||||
log.info("Docker VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_log(self):
|
||||
async def _get_log(self):
|
||||
"""
|
||||
Returns the log from the container
|
||||
|
||||
:returns: string
|
||||
"""
|
||||
|
||||
result = yield from self.manager.query("GET", "containers/{}/logs".format(self._cid), params={"stderr": 1, "stdout": 1})
|
||||
result = await self.manager.query("GET", "containers/{}/logs".format(self._cid), params={"stderr": 1, "stdout": 1})
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes the VM (including all its files).
|
||||
"""
|
||||
|
||||
yield from self.close()
|
||||
yield from super().delete()
|
||||
await self.close()
|
||||
await super().delete()
|
||||
@@ -34,9 +34,8 @@ import re
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
from gns3server.utils.interfaces import interfaces, is_interface_up
|
||||
from gns3server.utils.asyncio import wait_run_in_executor, asyncio_ensure_future
|
||||
from gns3server.utils.asyncio import wait_run_in_executor
|
||||
from gns3server.utils import parse_version
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from uuid import uuid4
|
||||
from ..base_manager import BaseManager
|
||||
from ..port_manager import PortManager
|
||||
@@ -166,17 +165,16 @@ class Dynamips(BaseManager):
|
||||
if dynamips_id in self._dynamips_ids[project_id]:
|
||||
self._dynamips_ids[project_id].remove(dynamips_id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unload(self):
|
||||
async def unload(self):
|
||||
|
||||
yield from BaseManager.unload(self)
|
||||
await BaseManager.unload(self)
|
||||
|
||||
tasks = []
|
||||
for device in self._devices.values():
|
||||
tasks.append(asyncio_ensure_future(device.hypervisor.stop()))
|
||||
tasks.append(asyncio.ensure_future(device.hypervisor.stop()))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@@ -184,37 +182,35 @@ class Dynamips(BaseManager):
|
||||
log.error("Could not stop device hypervisor {}".format(e), exc_info=1)
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closing(self, project):
|
||||
async def project_closing(self, project):
|
||||
"""
|
||||
Called when a project is about to be closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
|
||||
yield from super().project_closing(project)
|
||||
await super().project_closing(project)
|
||||
# delete the Dynamips devices corresponding to the project
|
||||
tasks = []
|
||||
for device in self._devices.values():
|
||||
if device.project.id == project.id:
|
||||
tasks.append(asyncio_ensure_future(device.delete()))
|
||||
tasks.append(asyncio.ensure_future(device.delete()))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
except (Exception, GeneratorExit) as e:
|
||||
log.error("Could not delete device {}".format(e), exc_info=1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
yield from super().project_closed(project)
|
||||
await super().project_closed(project)
|
||||
# delete useless Dynamips files
|
||||
project_dir = project.module_working_path(self.module_name.lower())
|
||||
|
||||
@@ -230,7 +226,7 @@ class Dynamips(BaseManager):
|
||||
log.debug("Deleting file {}".format(file))
|
||||
if file in self._ghost_files:
|
||||
self._ghost_files.remove(file)
|
||||
yield from wait_run_in_executor(os.remove, file)
|
||||
await wait_run_in_executor(os.remove, file)
|
||||
except OSError as e:
|
||||
log.warning("Could not delete file {}: {}".format(file, e))
|
||||
continue
|
||||
@@ -267,8 +263,7 @@ class Dynamips(BaseManager):
|
||||
self._dynamips_path = dynamips_path
|
||||
return dynamips_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_new_hypervisor(self, working_dir=None):
|
||||
async def start_new_hypervisor(self, working_dir=None):
|
||||
"""
|
||||
Creates a new Dynamips process and start it.
|
||||
|
||||
@@ -306,27 +301,25 @@ class Dynamips(BaseManager):
|
||||
hypervisor = Hypervisor(self._dynamips_path, working_dir, server_host, port, port_manager.console_host)
|
||||
|
||||
log.info("Creating new hypervisor {}:{} with working directory {}".format(hypervisor.host, hypervisor.port, working_dir))
|
||||
yield from hypervisor.start()
|
||||
await hypervisor.start()
|
||||
log.info("Hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
|
||||
yield from hypervisor.connect()
|
||||
await hypervisor.connect()
|
||||
if parse_version(hypervisor.version) < parse_version('0.2.11'):
|
||||
raise DynamipsError("Dynamips version must be >= 0.2.11, detected version is {}".format(hypervisor.version))
|
||||
|
||||
return hypervisor
|
||||
|
||||
@asyncio.coroutine
|
||||
def ghost_ios_support(self, vm):
|
||||
async def ghost_ios_support(self, vm):
|
||||
|
||||
ghost_ios_support = self.config.get_section_config("Dynamips").getboolean("ghost_ios_support", True)
|
||||
if ghost_ios_support:
|
||||
with (yield from Dynamips._ghost_ios_lock):
|
||||
async with Dynamips._ghost_ios_lock:
|
||||
try:
|
||||
yield from self._set_ghost_ios(vm)
|
||||
await self._set_ghost_ios(vm)
|
||||
except GeneratorExit:
|
||||
log.warning("Could not create ghost IOS image {} (GeneratorExit)".format(vm.name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_nio(self, node, nio_settings):
|
||||
async def create_nio(self, node, nio_settings):
|
||||
"""
|
||||
Creates a new NIO.
|
||||
|
||||
@@ -394,11 +387,10 @@ class Dynamips(BaseManager):
|
||||
else:
|
||||
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_settings["type"]))
|
||||
|
||||
yield from nio.create()
|
||||
await nio.create()
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_ghost_ios(self, vm):
|
||||
async def _set_ghost_ios(self, vm):
|
||||
"""
|
||||
Manages Ghost IOS support.
|
||||
|
||||
@@ -421,29 +413,28 @@ class Dynamips(BaseManager):
|
||||
ghost_id = str(uuid4())
|
||||
ghost = Router("ghost-" + ghost_file, ghost_id, vm.project, vm.manager, platform=vm.platform, hypervisor=vm.hypervisor, ghost_flag=True)
|
||||
try:
|
||||
yield from ghost.create()
|
||||
yield from ghost.set_image(vm.image)
|
||||
yield from ghost.set_ghost_status(1)
|
||||
yield from ghost.set_ghost_file(ghost_file_path)
|
||||
yield from ghost.set_ram(vm.ram)
|
||||
await ghost.create()
|
||||
await ghost.set_image(vm.image)
|
||||
await ghost.set_ghost_status(1)
|
||||
await ghost.set_ghost_file(ghost_file_path)
|
||||
await ghost.set_ram(vm.ram)
|
||||
try:
|
||||
yield from ghost.start()
|
||||
yield from ghost.stop()
|
||||
await ghost.start()
|
||||
await ghost.stop()
|
||||
self._ghost_files.add(ghost_file_path)
|
||||
except DynamipsError:
|
||||
raise
|
||||
finally:
|
||||
yield from ghost.clean_delete()
|
||||
await ghost.clean_delete()
|
||||
except DynamipsError as e:
|
||||
log.warning("Could not create ghost instance: {}".format(e))
|
||||
|
||||
if vm.ghost_file != ghost_file and os.path.isfile(ghost_file_path):
|
||||
# set the ghost file to the router
|
||||
yield from vm.set_ghost_status(2)
|
||||
yield from vm.set_ghost_file(ghost_file_path)
|
||||
await vm.set_ghost_status(2)
|
||||
await vm.set_ghost_file(ghost_file_path)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_vm_settings(self, vm, settings):
|
||||
async def update_vm_settings(self, vm, settings):
|
||||
"""
|
||||
Updates the VM settings.
|
||||
|
||||
@@ -455,23 +446,23 @@ class Dynamips(BaseManager):
|
||||
if hasattr(vm, name) and getattr(vm, name) != value:
|
||||
if hasattr(vm, "set_{}".format(name)):
|
||||
setter = getattr(vm, "set_{}".format(name))
|
||||
yield from setter(value)
|
||||
await setter(value)
|
||||
elif name.startswith("slot") and value in ADAPTER_MATRIX:
|
||||
slot_id = int(name[-1])
|
||||
adapter_name = value
|
||||
adapter = ADAPTER_MATRIX[adapter_name]()
|
||||
try:
|
||||
if vm.slots[slot_id] and not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
await vm.slot_remove_binding(slot_id)
|
||||
if not isinstance(vm.slots[slot_id], type(adapter)):
|
||||
yield from vm.slot_add_binding(slot_id, adapter)
|
||||
await vm.slot_add_binding(slot_id, adapter)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("slot") and (value is None or value is ""):
|
||||
slot_id = int(name[-1])
|
||||
try:
|
||||
if vm.slots[slot_id]:
|
||||
yield from vm.slot_remove_binding(slot_id)
|
||||
await vm.slot_remove_binding(slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("Slot {} doesn't exist on this router".format(slot_id))
|
||||
elif name.startswith("wic") and value in WIC_MATRIX:
|
||||
@@ -480,32 +471,31 @@ class Dynamips(BaseManager):
|
||||
wic = WIC_MATRIX[wic_name]()
|
||||
try:
|
||||
if vm.slots[0].wics[wic_slot_id] and not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
await vm.uninstall_wic(wic_slot_id)
|
||||
if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
|
||||
yield from vm.install_wic(wic_slot_id, wic)
|
||||
await vm.install_wic(wic_slot_id, wic)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
elif name.startswith("wic") and (value is None or value is ""):
|
||||
wic_slot_id = int(name[-1])
|
||||
try:
|
||||
if vm.slots[0].wics and vm.slots[0].wics[wic_slot_id]:
|
||||
yield from vm.uninstall_wic(wic_slot_id)
|
||||
await vm.uninstall_wic(wic_slot_id)
|
||||
except IndexError:
|
||||
raise DynamipsError("WIC slot {} doesn't exist on this router".format(wic_slot_id))
|
||||
|
||||
mmap_support = self.config.get_section_config("Dynamips").getboolean("mmap_support", True)
|
||||
if mmap_support is False:
|
||||
yield from vm.set_mmap(False)
|
||||
await vm.set_mmap(False)
|
||||
|
||||
sparse_memory_support = self.config.get_section_config("Dynamips").getboolean("sparse_memory_support", True)
|
||||
if sparse_memory_support is False:
|
||||
yield from vm.set_sparsemem(False)
|
||||
await vm.set_sparsemem(False)
|
||||
|
||||
# update the configs if needed
|
||||
yield from self.set_vm_configs(vm, settings)
|
||||
await self.set_vm_configs(vm, settings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_vm_configs(self, vm, settings):
|
||||
async def set_vm_configs(self, vm, settings):
|
||||
"""
|
||||
Set VM configs from pushed content or existing config files.
|
||||
|
||||
@@ -554,25 +544,24 @@ class Dynamips(BaseManager):
|
||||
|
||||
return os.path.join("configs", os.path.basename(path))
|
||||
|
||||
@asyncio.coroutine
|
||||
def auto_idlepc(self, vm):
|
||||
async def auto_idlepc(self, vm):
|
||||
"""
|
||||
Try to find the best possible idle-pc value.
|
||||
|
||||
:param vm: VM instance
|
||||
"""
|
||||
|
||||
yield from vm.set_idlepc("0x0")
|
||||
await vm.set_idlepc("0x0")
|
||||
was_auto_started = False
|
||||
old_priority = None
|
||||
try:
|
||||
status = yield from vm.get_status()
|
||||
status = await vm.get_status()
|
||||
if status != "running":
|
||||
yield from vm.start()
|
||||
await vm.start()
|
||||
was_auto_started = True
|
||||
yield from asyncio.sleep(20) # leave time to the router to boot
|
||||
await asyncio.sleep(20) # leave time to the router to boot
|
||||
validated_idlepc = None
|
||||
idlepcs = yield from vm.get_idle_pc_prop()
|
||||
idlepcs = await vm.get_idle_pc_prop()
|
||||
if not idlepcs:
|
||||
raise DynamipsError("No Idle-PC values found")
|
||||
|
||||
@@ -582,14 +571,14 @@ class Dynamips(BaseManager):
|
||||
match = re.search(r"^0x[0-9a-f]{8}$", idlepc.split()[0])
|
||||
if not match:
|
||||
continue
|
||||
yield from vm.set_idlepc(idlepc.split()[0])
|
||||
await vm.set_idlepc(idlepc.split()[0])
|
||||
log.debug("Auto Idle-PC: trying idle-PC value {}".format(vm.idlepc))
|
||||
start_time = time.time()
|
||||
initial_cpu_usage = yield from vm.get_cpu_usage()
|
||||
initial_cpu_usage = await vm.get_cpu_usage()
|
||||
log.debug("Auto Idle-PC: initial CPU usage is {}%".format(initial_cpu_usage))
|
||||
yield from asyncio.sleep(3) # wait 3 seconds to probe the cpu again
|
||||
await asyncio.sleep(3) # wait 3 seconds to probe the cpu again
|
||||
elapsed_time = time.time() - start_time
|
||||
cpu_usage = yield from vm.get_cpu_usage()
|
||||
cpu_usage = await vm.get_cpu_usage()
|
||||
cpu_elapsed_usage = cpu_usage - initial_cpu_usage
|
||||
cpu_usage = abs(cpu_elapsed_usage * 100.0 / elapsed_time)
|
||||
if cpu_usage > 100:
|
||||
@@ -609,11 +598,10 @@ class Dynamips(BaseManager):
|
||||
if old_priority is not None:
|
||||
vm.set_process_priority_windows(vm.hypervisor.process.pid, old_priority)
|
||||
if was_auto_started:
|
||||
yield from vm.stop()
|
||||
await vm.stop()
|
||||
return validated_idlepc
|
||||
|
||||
@asyncio.coroutine
|
||||
def duplicate_node(self, source_node_id, destination_node_id):
|
||||
async def duplicate_node(self, source_node_id, destination_node_id):
|
||||
"""
|
||||
Duplicate a node
|
||||
|
||||
@@ -626,7 +614,7 @@ class Dynamips(BaseManager):
|
||||
|
||||
# Not a Dynamips router
|
||||
if not hasattr(source_node, "startup_config_path"):
|
||||
return (yield from super().duplicate_node(source_node_id, destination_node_id))
|
||||
return (await super().duplicate_node(source_node_id, destination_node_id))
|
||||
|
||||
try:
|
||||
with open(source_node.startup_config_path) as f:
|
||||
@@ -638,13 +626,13 @@ class Dynamips(BaseManager):
|
||||
private_config = f.read()
|
||||
except OSError:
|
||||
private_config = None
|
||||
yield from self.set_vm_configs(destination_node, {
|
||||
await self.set_vm_configs(destination_node, {
|
||||
"startup_config_content": startup_config,
|
||||
"private_config_content": private_config
|
||||
})
|
||||
|
||||
# Force refresh of the name in configuration files
|
||||
new_name = destination_node.name
|
||||
yield from destination_node.set_name(source_node.name)
|
||||
yield from destination_node.set_name(new_name)
|
||||
await destination_node.set_name(source_node.name)
|
||||
await destination_node.set_name(new_name)
|
||||
return destination_node
|
||||
@@ -59,8 +59,7 @@ class DynamipsHypervisor:
|
||||
self._writer = None
|
||||
self._io_lock = asyncio.Lock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def connect(self, timeout=10):
|
||||
async def connect(self, timeout=10):
|
||||
"""
|
||||
Connects to the hypervisor.
|
||||
"""
|
||||
@@ -78,9 +77,9 @@ class DynamipsHypervisor:
|
||||
connection_success = False
|
||||
last_exception = None
|
||||
while time.time() - begin < timeout:
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
self._reader, self._writer = yield from asyncio.wait_for(asyncio.open_connection(host, self._port), timeout=1)
|
||||
self._reader, self._writer = await asyncio.wait_for(asyncio.open_connection(host, self._port), timeout=1)
|
||||
except (asyncio.TimeoutError, OSError) as e:
|
||||
last_exception = e
|
||||
continue
|
||||
@@ -93,13 +92,13 @@ class DynamipsHypervisor:
|
||||
log.info("Connected to Dynamips hypervisor on {}:{} after {:.4f} seconds".format(host, self._port, time.time() - begin))
|
||||
|
||||
try:
|
||||
version = yield from self.send("hypervisor version")
|
||||
version = await self.send("hypervisor version")
|
||||
self._version = version[0].split("-", 1)[0]
|
||||
except IndexError:
|
||||
self._version = "Unknown"
|
||||
|
||||
# this forces to send the working dir to Dynamips
|
||||
yield from self.set_working_dir(self._working_dir)
|
||||
await self.set_working_dir(self._working_dir)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
@@ -111,45 +110,41 @@ class DynamipsHypervisor:
|
||||
|
||||
return self._version
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes the connection to this hypervisor (but leave it running).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor close")
|
||||
await self.send("hypervisor close")
|
||||
self._writer.close()
|
||||
self._reader, self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this hypervisor (will no longer run).
|
||||
"""
|
||||
|
||||
try:
|
||||
# try to properly stop the hypervisor
|
||||
yield from self.send("hypervisor stop")
|
||||
await self.send("hypervisor stop")
|
||||
except DynamipsError:
|
||||
pass
|
||||
try:
|
||||
if self._writer is not None:
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
self._writer.close()
|
||||
except OSError as e:
|
||||
log.debug("Stopping hypervisor {}:{} {}".format(self._host, self._port, e))
|
||||
self._reader = self._writer = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset(self):
|
||||
async def reset(self):
|
||||
"""
|
||||
Resets this hypervisor (used to get an empty configuration).
|
||||
"""
|
||||
|
||||
yield from self.send("hypervisor reset")
|
||||
await self.send("hypervisor reset")
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_working_dir(self, working_dir):
|
||||
async def set_working_dir(self, working_dir):
|
||||
"""
|
||||
Sets the working directory for this hypervisor.
|
||||
|
||||
@@ -157,7 +152,7 @@ class DynamipsHypervisor:
|
||||
"""
|
||||
|
||||
# encase working_dir in quotes to protect spaces in the path
|
||||
yield from self.send('hypervisor working_dir "{}"'.format(working_dir))
|
||||
await self.send('hypervisor working_dir "{}"'.format(working_dir))
|
||||
self._working_dir = working_dir
|
||||
log.debug("Working directory set to {}".format(self._working_dir))
|
||||
|
||||
@@ -221,8 +216,7 @@ class DynamipsHypervisor:
|
||||
|
||||
self._host = host
|
||||
|
||||
@asyncio.coroutine
|
||||
def send(self, command):
|
||||
async def send(self, command):
|
||||
"""
|
||||
Sends commands to this hypervisor.
|
||||
|
||||
@@ -244,7 +238,7 @@ class DynamipsHypervisor:
|
||||
# but still have more data. The only thing we know for sure is the last line
|
||||
# will begin with '100-' or a '2xx-' and end with '\r\n'
|
||||
|
||||
with (yield from self._io_lock):
|
||||
async with self._io_lock:
|
||||
if self._writer is None or self._reader is None:
|
||||
raise DynamipsError("Not connected")
|
||||
|
||||
@@ -252,7 +246,7 @@ class DynamipsHypervisor:
|
||||
command = command.strip() + '\n'
|
||||
log.debug("sending {}".format(command))
|
||||
self._writer.write(command.encode())
|
||||
yield from self._writer.drain()
|
||||
await self._writer.drain()
|
||||
except OSError as e:
|
||||
raise DynamipsError("Could not send Dynamips command '{command}' to {host}:{port}: {error}, process running: {run}"
|
||||
.format(command=command.strip(), host=self._host, port=self._port, error=e, run=self.is_running()))
|
||||
@@ -265,8 +259,8 @@ class DynamipsHypervisor:
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
# line = yield from self._reader.readline() # this can lead to ValueError: Line is too long
|
||||
chunk = yield from self._reader.read(1024) # match to Dynamips' buffer size
|
||||
# line = await self._reader.readline() # this can lead to ValueError: Line is too long
|
||||
chunk = await self._reader.read(1024) # match to Dynamips' buffer size
|
||||
except asyncio.CancelledError:
|
||||
# task has been canceled but continue to read
|
||||
# any remaining data sent by the hypervisor
|
||||
@@ -283,7 +277,7 @@ class DynamipsHypervisor:
|
||||
.format(host=self._host, port=self._port, run=self.is_running()))
|
||||
else:
|
||||
retries += 1
|
||||
yield from asyncio.sleep(0.1)
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
retries = 0
|
||||
buf += chunk.decode("utf-8", errors="ignore")
|
||||
|
||||
@@ -111,8 +111,7 @@ class Hypervisor(DynamipsHypervisor):
|
||||
|
||||
self._path = path
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the Dynamips hypervisor process.
|
||||
"""
|
||||
@@ -129,7 +128,7 @@ class Hypervisor(DynamipsHypervisor):
|
||||
self._stdout_file = os.path.join(self.working_dir, "dynamips_i{}_stdout.txt".format(self._id))
|
||||
log.info("Dynamips process logging to {}".format(self._stdout_file))
|
||||
with open(self._stdout_file, "w", encoding="utf-8") as fd:
|
||||
self._process = yield from asyncio.create_subprocess_exec(*self._command,
|
||||
self._process = await asyncio.create_subprocess_exec(*self._command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self._working_dir,
|
||||
@@ -140,20 +139,19 @@ class Hypervisor(DynamipsHypervisor):
|
||||
log.error("Could not start Dynamips: {}".format(e))
|
||||
raise DynamipsError("Could not start Dynamips: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the Dynamips hypervisor process.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
log.info("Stopping Dynamips process PID={}".format(self._process.pid))
|
||||
yield from DynamipsHypervisor.stop(self)
|
||||
await DynamipsHypervisor.stop(self)
|
||||
# give some time for the hypervisor to properly stop.
|
||||
# time to delete UNIX NIOs for instance.
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
log.warning("Dynamips process {} is still running... killing it".format(self._process.pid))
|
||||
|
||||
@@ -48,53 +48,48 @@ class NIO:
|
||||
self._output_filter_options = None # no output filter options by default
|
||||
self._dynamips_direction = {"in": 0, "out": 1, "both": 2}
|
||||
|
||||
@asyncio.coroutine
|
||||
def list(self):
|
||||
async def list(self):
|
||||
"""
|
||||
Returns all NIOs.
|
||||
|
||||
:returns: NIO list
|
||||
"""
|
||||
|
||||
nio_list = yield from self._hypervisor.send("nio list")
|
||||
nio_list = await self._hypervisor.send("nio list")
|
||||
return nio_list
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this NIO.
|
||||
"""
|
||||
|
||||
if self._input_filter or self._output_filter:
|
||||
yield from self.unbind_filter("both")
|
||||
yield from self._hypervisor.send("nio delete {}".format(self._name))
|
||||
await self.unbind_filter("both")
|
||||
await self._hypervisor.send("nio delete {}".format(self._name))
|
||||
log.info("NIO {name} has been deleted".format(name=self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def rename(self, new_name):
|
||||
async def rename(self, new_name):
|
||||
"""
|
||||
Renames this NIO
|
||||
|
||||
:param new_name: new NIO name
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, new_name=new_name))
|
||||
|
||||
log.info("NIO {name} renamed to {new_name}".format(name=self._name, new_name=new_name))
|
||||
self._name = new_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def debug(self, debug):
|
||||
async def debug(self, debug):
|
||||
"""
|
||||
Enables/Disables debugging for this NIO.
|
||||
|
||||
:param debug: debug value (0 = disable, enable = 1)
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio set_debug {name} {debug}".format(name=self._name, debug=debug))
|
||||
await self._hypervisor.send("nio set_debug {name} {debug}".format(name=self._name, debug=debug))
|
||||
|
||||
@asyncio.coroutine
|
||||
def bind_filter(self, direction, filter_name):
|
||||
async def bind_filter(self, direction, filter_name):
|
||||
"""
|
||||
Adds a packet filter to this NIO.
|
||||
Filter "freq_drop" drops packets.
|
||||
@@ -108,7 +103,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to bind filter {}:".format(direction, filter_name))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio bind_filter {name} {direction} {filter}".format(name=self._name,
|
||||
await self._hypervisor.send("nio bind_filter {name} {direction} {filter}".format(name=self._name,
|
||||
direction=dynamips_direction,
|
||||
filter=filter_name))
|
||||
|
||||
@@ -120,8 +115,7 @@ class NIO:
|
||||
self._input_filter = filter_name
|
||||
self._output_filter = filter_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def unbind_filter(self, direction):
|
||||
async def unbind_filter(self, direction):
|
||||
"""
|
||||
Removes packet filter for this NIO.
|
||||
|
||||
@@ -132,7 +126,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to unbind filter:".format(direction))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio unbind_filter {name} {direction}".format(name=self._name,
|
||||
await self._hypervisor.send("nio unbind_filter {name} {direction}".format(name=self._name,
|
||||
direction=dynamips_direction))
|
||||
|
||||
if direction == "in":
|
||||
@@ -143,8 +137,7 @@ class NIO:
|
||||
self._input_filter = None
|
||||
self._output_filter = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def setup_filter(self, direction, options):
|
||||
async def setup_filter(self, direction, options):
|
||||
"""
|
||||
Setups a packet filter bound with this NIO.
|
||||
|
||||
@@ -166,7 +159,7 @@ class NIO:
|
||||
raise DynamipsError("Unknown direction {} to setup filter:".format(direction))
|
||||
dynamips_direction = self._dynamips_direction[direction]
|
||||
|
||||
yield from self._hypervisor.send("nio setup_filter {name} {direction} {options}".format(name=self._name,
|
||||
await self._hypervisor.send("nio setup_filter {name} {direction} {options}".format(name=self._name,
|
||||
direction=dynamips_direction,
|
||||
options=options))
|
||||
|
||||
@@ -198,24 +191,22 @@ class NIO:
|
||||
|
||||
return self._output_filter, self._output_filter_options
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_stats(self):
|
||||
async def get_stats(self):
|
||||
"""
|
||||
Gets statistics for this NIO.
|
||||
|
||||
:returns: NIO statistics (string with packets in, packets out, bytes in, bytes out)
|
||||
"""
|
||||
|
||||
stats = yield from self._hypervisor.send("nio get_stats {}".format(self._name))
|
||||
stats = await self._hypervisor.send("nio get_stats {}".format(self._name))
|
||||
return stats[0]
|
||||
|
||||
@asyncio.coroutine
|
||||
def reset_stats(self):
|
||||
async def reset_stats(self):
|
||||
"""
|
||||
Resets statistics for this NIO.
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio reset_stats {}".format(self._name))
|
||||
await self._hypervisor.send("nio reset_stats {}".format(self._name))
|
||||
|
||||
@property
|
||||
def bandwidth(self):
|
||||
@@ -227,15 +218,14 @@ class NIO:
|
||||
|
||||
return self._bandwidth
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_bandwidth(self, bandwidth):
|
||||
async def set_bandwidth(self, bandwidth):
|
||||
"""
|
||||
Sets bandwidth constraint.
|
||||
|
||||
:param bandwidth: bandwidth integer value (in Kb/s)
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send("nio set_bandwidth {name} {bandwidth}".format(name=self._name, bandwidth=bandwidth))
|
||||
await self._hypervisor.send("nio set_bandwidth {name} {bandwidth}".format(name=self._name, bandwidth=bandwidth))
|
||||
self._bandwidth = bandwidth
|
||||
|
||||
@property
|
||||
|
||||
@@ -43,10 +43,9 @@ class NIOGenericEthernet(NIO):
|
||||
self._ethernet_device = ethernet_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name,
|
||||
eth_device=self._ethernet_device))
|
||||
|
||||
log.info("NIO Generic Ethernet {name} created with device {device}".format(name=self._name,
|
||||
|
||||
@@ -42,10 +42,9 @@ class NIOLinuxEthernet(NIO):
|
||||
self._ethernet_device = ethernet_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name,
|
||||
eth_device=self._ethernet_device))
|
||||
|
||||
log.info("NIO Linux Ethernet {name} created with device {device}".format(name=self._name,
|
||||
|
||||
@@ -41,10 +41,9 @@ class NIONull(NIO):
|
||||
name = 'null-{}'.format(uuid.uuid4())
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_null {}".format(self._name))
|
||||
await self._hypervisor.send("nio create_null {}".format(self._name))
|
||||
log.info("NIO NULL {name} created.".format(name=self._name))
|
||||
|
||||
def __json__(self):
|
||||
|
||||
@@ -43,10 +43,9 @@ class NIOTAP(NIO):
|
||||
self._tap_device = tap_device
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, tap=self._tap_device))
|
||||
await self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, tap=self._tap_device))
|
||||
log.info("NIO TAP {name} created with device {device}".format(name=self._name, device=self._tap_device))
|
||||
|
||||
@property
|
||||
|
||||
@@ -53,13 +53,12 @@ class NIOUDP(NIO):
|
||||
self._node = node
|
||||
super().__init__(name, node.hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
if not self._hypervisor:
|
||||
return
|
||||
# Ubridge is not supported
|
||||
if not hasattr(self._node, "add_ubridge_udp_connection"):
|
||||
yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
lport=self._lport,
|
||||
rhost=self._rhost,
|
||||
rport=self._rport))
|
||||
@@ -67,7 +66,7 @@ class NIOUDP(NIO):
|
||||
self._local_tunnel_lport = self._node.manager.port_manager.get_free_udp_port(self._node.project)
|
||||
self._local_tunnel_rport = self._node.manager.port_manager.get_free_udp_port(self._node.project)
|
||||
self._bridge_name = 'DYNAMIPS-{}-{}'.format(self._local_tunnel_lport, self._local_tunnel_rport)
|
||||
yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
|
||||
lport=self._local_tunnel_lport,
|
||||
rhost='127.0.0.1',
|
||||
rport=self._local_tunnel_rport))
|
||||
@@ -84,24 +83,22 @@ class NIOUDP(NIO):
|
||||
self._rhost,
|
||||
self._rport)
|
||||
self._destination_nio.filters = self._filters
|
||||
yield from self._node.add_ubridge_udp_connection(
|
||||
await self._node.add_ubridge_udp_connection(
|
||||
self._bridge_name,
|
||||
self._source_nio,
|
||||
self._destination_nio
|
||||
)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self):
|
||||
async def update(self):
|
||||
self._destination_nio.filters = self._filters
|
||||
yield from self._node.update_ubridge_udp_connection(
|
||||
await self._node.update_ubridge_udp_connection(
|
||||
self._bridge_name,
|
||||
self._source_nio,
|
||||
self._destination_nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
if self._local_tunnel_lport:
|
||||
yield from self._node.ubridge_delete_bridge(self._bridge_name)
|
||||
await self._node.ubridge_delete_bridge(self._bridge_name)
|
||||
self._node.manager.port_manager.release_udp_port(self._local_tunnel_lport, self ._node.project)
|
||||
if self._local_tunnel_rport:
|
||||
self._node.manager.port_manager.release_udp_port(self._local_tunnel_rport, self._node.project)
|
||||
|
||||
@@ -45,10 +45,9 @@ class NIOUNIX(NIO):
|
||||
self._remote_file = remote_file
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name,
|
||||
local=self._local_file,
|
||||
remote=self._remote_file))
|
||||
|
||||
|
||||
@@ -45,10 +45,9 @@ class NIOVDE(NIO):
|
||||
self._local_file = local_file
|
||||
super().__init__(name, hypervisor)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name,
|
||||
await self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name,
|
||||
control=self._control_file,
|
||||
local=self._local_file))
|
||||
|
||||
|
||||
@@ -64,26 +64,24 @@ class ATMSwitch(Device):
|
||||
"mappings": mappings,
|
||||
"status": "started"}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('atmsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('atmsw create "{}"'.format(self._name))
|
||||
log.info('ATM switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this ATM switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('atm rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('atm rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('ATM switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@@ -119,31 +117,29 @@ class ATMSwitch(Device):
|
||||
|
||||
self._mappings = mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('atmsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('atmsw delete "{}"'.format(self._name))
|
||||
log.info('ATM switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete ATM switch {}".format(self._name))
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this ATM switch.
|
||||
"""
|
||||
yield from self.close()
|
||||
await self.close()
|
||||
|
||||
def has_port(self, port):
|
||||
"""
|
||||
@@ -156,8 +152,7 @@ class ATMSwitch(Device):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on ATM switch.
|
||||
|
||||
@@ -174,10 +169,9 @@ class ATMSwitch(Device):
|
||||
port=port_number))
|
||||
|
||||
self._nios[port_number] = nio
|
||||
yield from self.set_mappings(self._mappings)
|
||||
await self.set_mappings(self._mappings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this ATM switch.
|
||||
|
||||
@@ -202,8 +196,8 @@ class ATMSwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi,
|
||||
destination_vci=destination_vci))
|
||||
yield from self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
yield from self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
await self.unmap_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
await self.unmap_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
else:
|
||||
# remove the virtual paths mapped with this port/nio
|
||||
source_port, source_vpi = source
|
||||
@@ -215,8 +209,8 @@ class ATMSwitch(Device):
|
||||
source_vpi=source_vpi,
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi))
|
||||
yield from self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
yield from self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
await self.unmap_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
await self.unmap_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
|
||||
nio = self._nios[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -229,8 +223,7 @@ class ATMSwitch(Device):
|
||||
del self._nios[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_mappings(self, mappings):
|
||||
async def set_mappings(self, mappings):
|
||||
"""
|
||||
Applies VC mappings
|
||||
|
||||
@@ -258,8 +251,8 @@ class ATMSwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi,
|
||||
destination_vci=destination_vci))
|
||||
yield from self.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
yield from self.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
await self.map_pvc(source_port, source_vpi, source_vci, destination_port, destination_vpi, destination_vci)
|
||||
await self.map_pvc(destination_port, destination_vpi, destination_vci, source_port, source_vpi, source_vci)
|
||||
else:
|
||||
# add the virtual paths
|
||||
source_port, source_vpi = map(int, source.split(':'))
|
||||
@@ -272,11 +265,10 @@ class ATMSwitch(Device):
|
||||
source_vpi=source_vpi,
|
||||
destination_port=destination_port,
|
||||
destination_vpi=destination_vpi))
|
||||
yield from self.map_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
yield from self.map_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
await self.map_vp(source_port, source_vpi, destination_port, destination_vpi)
|
||||
await self.map_vp(destination_port, destination_vpi, source_port, source_vpi)
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_vp(self, port1, vpi1, port2, vpi2):
|
||||
async def map_vp(self, port1, vpi1, port2, vpi2):
|
||||
"""
|
||||
Creates a new Virtual Path connection.
|
||||
|
||||
@@ -295,7 +287,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw create_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw create_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
output_nio=nio2,
|
||||
@@ -310,8 +302,7 @@ class ATMSwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, vpi1)] = (port2, vpi2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_vp(self, port1, vpi1, port2, vpi2):
|
||||
async def unmap_vp(self, port1, vpi1, port2, vpi2):
|
||||
"""
|
||||
Deletes a new Virtual Path connection.
|
||||
|
||||
@@ -330,7 +321,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw delete_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw delete_vpc "{name}" {input_nio} {input_vpi} {output_nio} {output_vpi}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
output_nio=nio2,
|
||||
@@ -345,8 +336,7 @@ class ATMSwitch(Device):
|
||||
|
||||
del self._active_mappings[(port1, vpi1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
async def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
"""
|
||||
Creates a new Virtual Channel connection (unidirectional).
|
||||
|
||||
@@ -367,7 +357,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw create_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw create_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
input_vci=vci1,
|
||||
@@ -386,8 +376,7 @@ class ATMSwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, vpi1, vci1)] = (port2, vpi2, vci2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
async def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
|
||||
"""
|
||||
Deletes a new Virtual Channel connection (unidirectional).
|
||||
|
||||
@@ -408,7 +397,7 @@ class ATMSwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('atmsw delete_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
await self._hypervisor.send('atmsw delete_vcc "{name}" {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_vpi=vpi1,
|
||||
input_vci=vci1,
|
||||
@@ -426,8 +415,7 @@ class ATMSwitch(Device):
|
||||
vci2=vci2))
|
||||
del self._active_mappings[(port1, vpi1, vci1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -448,15 +436,14 @@ class ATMSwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('ATM switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -467,7 +454,7 @@ class ATMSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('ATM switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
@@ -41,25 +41,23 @@ class Bridge(Device):
|
||||
super().__init__(name, node_id, project, manager, hypervisor)
|
||||
self._nios = []
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('nio_bridge create "{}"'.format(self._name))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this bridge.
|
||||
|
||||
:param new_name: New name for this bridge
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge rename "{name}" "{new_name}"'.format(name=self._name,
|
||||
await self._hypervisor.send('nio_bridge rename "{name}" "{new_name}"'.format(name=self._name,
|
||||
new_name=new_name))
|
||||
|
||||
self._name = new_name
|
||||
@@ -74,8 +72,7 @@ class Bridge(Device):
|
||||
|
||||
return self._nios
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this bridge.
|
||||
"""
|
||||
@@ -83,28 +80,26 @@ class Bridge(Device):
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self._hypervisor.send('nio_bridge delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('nio_bridge delete "{}"'.format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio):
|
||||
async def add_nio(self, nio):
|
||||
"""
|
||||
Adds a NIO as new port on this bridge.
|
||||
|
||||
:param nio: NIO instance to add
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('nio_bridge add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('nio_bridge add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
self._nios.append(nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, nio):
|
||||
async def remove_nio(self, nio):
|
||||
"""
|
||||
Removes the specified NIO as member of this bridge.
|
||||
|
||||
:param nio: NIO instance to remove
|
||||
"""
|
||||
if self._hypervisor:
|
||||
yield from self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
self._nios.remove(nio)
|
||||
|
||||
@property
|
||||
|
||||
@@ -70,12 +70,11 @@ class C1700(Router):
|
||||
router_info.update(c1700_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "1720":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@@ -103,8 +102,7 @@ class C1700(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
@@ -112,7 +110,7 @@ class C1700(Router):
|
||||
1720, 1721, 1750, 1751 or 1760
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c1700 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c1700 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -131,15 +129,14 @@ class C1700(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c1700 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c1700 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -85,12 +85,11 @@ class C2600(Router):
|
||||
router_info.update(c2600_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "2610":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@@ -112,8 +111,7 @@ class C2600(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
@@ -122,7 +120,7 @@ class C2600(Router):
|
||||
2620XM, 2621XM, 2650XM or 2651XM
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c2600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -140,15 +138,14 @@ class C2600(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c2600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -79,15 +79,14 @@ class C2691(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c2691 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c2691 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -66,12 +66,11 @@ class C3600(Router):
|
||||
router_info.update(c3600_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
if self._chassis != "3640":
|
||||
yield from self.set_chassis(self._chassis)
|
||||
await self.set_chassis(self._chassis)
|
||||
self._setup_chassis()
|
||||
|
||||
def _setup_chassis(self):
|
||||
@@ -98,15 +97,14 @@ class C3600(Router):
|
||||
|
||||
return self._chassis
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_chassis(self, chassis):
|
||||
async def set_chassis(self, chassis):
|
||||
"""
|
||||
Sets the chassis.
|
||||
|
||||
:param: chassis string: 3620, 3640 or 3660
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
await self._hypervisor.send('c3600 set_chassis "{name}" {chassis}'.format(name=self._name, chassis=chassis))
|
||||
|
||||
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -125,15 +123,14 @@ class C3600(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Set I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3600 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -79,15 +79,14 @@ class C3725(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3725 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3725 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -79,15 +79,14 @@ class C3745(Router):
|
||||
|
||||
return self._iomem
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_iomem(self, iomem):
|
||||
async def set_iomem(self, iomem):
|
||||
"""
|
||||
Sets I/O memory size for this router.
|
||||
|
||||
:param iomem: I/O memory size
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c3745 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
await self._hypervisor.send('c3745 set_iomem "{name}" {size}'.format(name=self._name, size=iomem))
|
||||
|
||||
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
|
||||
id=self._id,
|
||||
|
||||
@@ -86,19 +86,18 @@ class C7200(Router):
|
||||
router_info.update(c7200_router_info)
|
||||
return router_info
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Router.create(self)
|
||||
await Router.create(self)
|
||||
|
||||
if self._npe != "npe-400":
|
||||
yield from self.set_npe(self._npe)
|
||||
await self.set_npe(self._npe)
|
||||
|
||||
# first slot is a mandatory Input/Output controller (based on NPE type)
|
||||
if self.npe == "npe-g2":
|
||||
yield from self.slot_add_binding(0, C7200_IO_GE_E())
|
||||
await self.slot_add_binding(0, C7200_IO_GE_E())
|
||||
else:
|
||||
yield from self.slot_add_binding(0, C7200_IO_FE())
|
||||
await self.slot_add_binding(0, C7200_IO_FE())
|
||||
|
||||
@property
|
||||
def npe(self):
|
||||
@@ -110,8 +109,7 @@ class C7200(Router):
|
||||
|
||||
return self._npe
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_npe(self, npe):
|
||||
async def set_npe(self, npe):
|
||||
"""
|
||||
Sets the NPE model.
|
||||
|
||||
@@ -120,10 +118,10 @@ class C7200(Router):
|
||||
npe-225, npe-300, npe-400 and npe-g2 (PowerPC c7200 only)
|
||||
"""
|
||||
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise DynamipsError("Cannot change NPE on running router")
|
||||
|
||||
yield from self._hypervisor.send('c7200 set_npe "{name}" {npe}'.format(name=self._name, npe=npe))
|
||||
await self._hypervisor.send('c7200 set_npe "{name}" {npe}'.format(name=self._name, npe=npe))
|
||||
|
||||
log.info('Router "{name}" [{id}]: NPE updated from {old_npe} to {new_npe}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -141,15 +139,14 @@ class C7200(Router):
|
||||
|
||||
return self._midplane
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_midplane(self, midplane):
|
||||
async def set_midplane(self, midplane):
|
||||
"""
|
||||
Sets the midplane model.
|
||||
|
||||
:returns: midplane model string (e.g. "vxr" or "std")
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('c7200 set_midplane "{name}" {midplane}'.format(name=self._name, midplane=midplane))
|
||||
await self._hypervisor.send('c7200 set_midplane "{name}" {midplane}'.format(name=self._name, midplane=midplane))
|
||||
|
||||
log.info('Router "{name}" [{id}]: midplane updated from {old_midplane} to {new_midplane}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -167,8 +164,7 @@ class C7200(Router):
|
||||
|
||||
return self._sensors
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_sensors(self, sensors):
|
||||
async def set_sensors(self, sensors):
|
||||
"""
|
||||
Sets the 4 sensors with temperature in degree Celcius.
|
||||
|
||||
@@ -182,7 +178,7 @@ class C7200(Router):
|
||||
|
||||
sensor_id = 0
|
||||
for sensor in sensors:
|
||||
yield from self._hypervisor.send('c7200 set_temp_sensor "{name}" {sensor_id} {temp}'.format(name=self._name,
|
||||
await self._hypervisor.send('c7200 set_temp_sensor "{name}" {sensor_id} {temp}'.format(name=self._name,
|
||||
sensor_id=sensor_id,
|
||||
temp=sensor))
|
||||
|
||||
@@ -205,8 +201,7 @@ class C7200(Router):
|
||||
|
||||
return self._power_supplies
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_power_supplies(self, power_supplies):
|
||||
async def set_power_supplies(self, power_supplies):
|
||||
"""
|
||||
Sets the 2 power supplies with 0 = off, 1 = on.
|
||||
|
||||
@@ -216,7 +211,7 @@ class C7200(Router):
|
||||
|
||||
power_supply_id = 0
|
||||
for power_supply in power_supplies:
|
||||
yield from self._hypervisor.send('c7200 set_power_supply "{name}" {power_supply_id} {powered_on}'.format(name=self._name,
|
||||
await self._hypervisor.send('c7200 set_power_supply "{name}" {power_supply_id} {powered_on}'.format(name=self._name,
|
||||
power_supply_id=power_supply_id,
|
||||
powered_on=power_supply))
|
||||
|
||||
@@ -228,8 +223,7 @@ class C7200(Router):
|
||||
|
||||
self._power_supplies = power_supplies
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this router.
|
||||
At least the IOS image must be set before starting it.
|
||||
@@ -237,8 +231,8 @@ class C7200(Router):
|
||||
|
||||
# trick: we must send sensors and power supplies info after starting the router
|
||||
# otherwise they are not taken into account (Dynamips bug?)
|
||||
yield from Router.start(self)
|
||||
await Router.start(self)
|
||||
if self._sensors != [22, 22, 22, 22]:
|
||||
yield from self.set_sensors(self._sensors)
|
||||
await self.set_sensors(self._sensors)
|
||||
if self._power_supplies != [1, 1]:
|
||||
yield from self.set_power_supplies(self._power_supplies)
|
||||
await self.set_power_supplies(self._power_supplies)
|
||||
@@ -93,10 +93,9 @@ class EthernetHub(Bridge):
|
||||
|
||||
self._ports = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from Bridge.create(self)
|
||||
await Bridge.create(self)
|
||||
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
@property
|
||||
@@ -109,32 +108,29 @@ class EthernetHub(Bridge):
|
||||
|
||||
return self._mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
return (yield from self.close())
|
||||
async def delete(self):
|
||||
return (await self.close())
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Deletes this hub.
|
||||
"""
|
||||
|
||||
for nio in self._nios:
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
try:
|
||||
yield from Bridge.delete(self)
|
||||
await Bridge.delete(self)
|
||||
log.info('Ethernet hub "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Ethernet hub {}".format(self._name))
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on this hub.
|
||||
|
||||
@@ -148,7 +144,7 @@ class EthernetHub(Bridge):
|
||||
if port_number in self._mappings:
|
||||
raise DynamipsError("Port {} isn't free".format(port_number))
|
||||
|
||||
yield from Bridge.add_nio(self, nio)
|
||||
await Bridge.add_nio(self, nio)
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -156,8 +152,7 @@ class EthernetHub(Bridge):
|
||||
port=port_number))
|
||||
self._mappings[port_number] = nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this hub.
|
||||
|
||||
@@ -172,7 +167,7 @@ class EthernetHub(Bridge):
|
||||
nio = self._mappings[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
yield from Bridge.remove_nio(self, nio)
|
||||
await Bridge.remove_nio(self, nio)
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -182,8 +177,7 @@ class EthernetHub(Bridge):
|
||||
del self._mappings[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -204,15 +198,14 @@ class EthernetHub(Bridge):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Ethernet hub "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -223,7 +216,7 @@ class EthernetHub(Bridge):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._mappings[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Ethernet hub "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
@@ -43,13 +43,12 @@ class EthernetSwitchConsole(EmbedShell):
|
||||
super().__init__(welcome_message="Welcome to GNS3 builtin Ethernet switch.\n\nType help for available commands\n")
|
||||
self._node = node
|
||||
|
||||
@asyncio.coroutine
|
||||
def mac(self):
|
||||
async def mac(self):
|
||||
"""
|
||||
Show MAC address table
|
||||
"""
|
||||
res = 'Port Mac VLAN\n'
|
||||
result = (yield from self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
||||
result = (await self._node._hypervisor.send('ethsw show_mac_addr_table {}'.format(self._node.name)))
|
||||
for line in result:
|
||||
mac, vlan, nio = line.replace(' ', ' ').split(' ')
|
||||
mac = mac.replace('.', '')
|
||||
@@ -163,41 +162,38 @@ class EthernetSwitch(Device):
|
||||
|
||||
self._ports = ports
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_port_settings(self):
|
||||
async def update_port_settings(self):
|
||||
for port_settings in self._ports:
|
||||
port_number = port_settings["port_number"]
|
||||
if port_number in self._nios and self._nios[port_number] is not None:
|
||||
yield from self.set_port_settings(port_number, port_settings)
|
||||
await self.set_port_settings(port_number, port_settings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw create "{}"'.format(self._name))
|
||||
log.info('Ethernet switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
|
||||
self._telnet_shell = EthernetSwitchConsole(self)
|
||||
self._telnet_shell.prompt = self._name + '> '
|
||||
self._telnet = create_telnet_shell(self._telnet_shell)
|
||||
try:
|
||||
self._telnet_server = (yield from asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
||||
self._telnet_server = (await asyncio.start_server(self._telnet.run, self._manager.port_manager.console_host, self.console))
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this Ethernet switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('ethsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('Ethernet switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@@ -223,39 +219,36 @@ class EthernetSwitch(Device):
|
||||
|
||||
return self._mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
return (yield from self.close())
|
||||
async def delete(self):
|
||||
return (await self.close())
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Deletes this Ethernet switch.
|
||||
"""
|
||||
|
||||
yield from self._telnet.close()
|
||||
await self._telnet.close()
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
self.manager.port_manager.release_tcp_port(self._console, self._project)
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('ethsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw delete "{}"'.format(self._name))
|
||||
log.info('Ethernet switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Ethernet switch {}".format(self._name))
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on Ethernet switch.
|
||||
|
||||
@@ -266,7 +259,7 @@ class EthernetSwitch(Device):
|
||||
if port_number in self._nios:
|
||||
raise DynamipsError("Port {} isn't free".format(port_number))
|
||||
|
||||
yield from self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('ethsw add_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -275,11 +268,10 @@ class EthernetSwitch(Device):
|
||||
self._nios[port_number] = nio
|
||||
for port_settings in self._ports:
|
||||
if port_settings["port_number"] == port_number:
|
||||
yield from self.set_port_settings(port_number, port_settings)
|
||||
await self.set_port_settings(port_number, port_settings)
|
||||
break
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this Ethernet switch.
|
||||
|
||||
@@ -295,7 +287,7 @@ class EthernetSwitch(Device):
|
||||
if isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
if self._hypervisor:
|
||||
yield from self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
await self._hypervisor.send('ethsw remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
@@ -308,8 +300,7 @@ class EthernetSwitch(Device):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_port_settings(self, port_number, settings):
|
||||
async def set_port_settings(self, port_number, settings):
|
||||
"""
|
||||
Applies port settings to a specific port.
|
||||
|
||||
@@ -318,14 +309,13 @@ class EthernetSwitch(Device):
|
||||
"""
|
||||
|
||||
if settings["type"] == "access":
|
||||
yield from self.set_access_port(port_number, settings["vlan"])
|
||||
await self.set_access_port(port_number, settings["vlan"])
|
||||
elif settings["type"] == "dot1q":
|
||||
yield from self.set_dot1q_port(port_number, settings["vlan"])
|
||||
await self.set_dot1q_port(port_number, settings["vlan"])
|
||||
elif settings["type"] == "qinq":
|
||||
yield from self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
|
||||
await self.set_qinq_port(port_number, settings["vlan"], settings.get("ethertype"))
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_access_port(self, port_number, vlan_id):
|
||||
async def set_access_port(self, port_number, vlan_id):
|
||||
"""
|
||||
Sets the specified port as an ACCESS port.
|
||||
|
||||
@@ -337,7 +327,7 @@ class EthernetSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_access_port "{name}" {nio} {vlan_id}'.format(name=self._name,
|
||||
nio=nio,
|
||||
vlan_id=vlan_id))
|
||||
|
||||
@@ -347,8 +337,7 @@ class EthernetSwitch(Device):
|
||||
vlan_id=vlan_id))
|
||||
self._mappings[port_number] = ("access", vlan_id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_dot1q_port(self, port_number, native_vlan):
|
||||
async def set_dot1q_port(self, port_number, native_vlan):
|
||||
"""
|
||||
Sets the specified port as a 802.1Q trunk port.
|
||||
|
||||
@@ -360,7 +349,7 @@ class EthernetSwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_dot1q_port "{name}" {nio} {native_vlan}'.format(name=self._name,
|
||||
nio=nio,
|
||||
native_vlan=native_vlan))
|
||||
|
||||
@@ -371,8 +360,7 @@ class EthernetSwitch(Device):
|
||||
|
||||
self._mappings[port_number] = ("dot1q", native_vlan)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_qinq_port(self, port_number, outer_vlan, ethertype):
|
||||
async def set_qinq_port(self, port_number, outer_vlan, ethertype):
|
||||
"""
|
||||
Sets the specified port as a trunk (QinQ) port.
|
||||
|
||||
@@ -387,7 +375,7 @@ class EthernetSwitch(Device):
|
||||
if ethertype != "0x8100" and parse_version(self.hypervisor.version) < parse_version('0.2.16'):
|
||||
raise DynamipsError("Dynamips version required is >= 0.2.16 to change the default QinQ Ethernet type, detected version is {}".format(self.hypervisor.version))
|
||||
|
||||
yield from self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
|
||||
await self._hypervisor.send('ethsw set_qinq_port "{name}" {nio} {outer_vlan} {ethertype}'.format(name=self._name,
|
||||
nio=nio,
|
||||
outer_vlan=outer_vlan,
|
||||
ethertype=ethertype if ethertype != "0x8100" else ""))
|
||||
@@ -399,27 +387,24 @@ class EthernetSwitch(Device):
|
||||
ethertype=ethertype))
|
||||
self._mappings[port_number] = ("qinq", outer_vlan, ethertype)
|
||||
|
||||
@asyncio.coroutine
|
||||
def get_mac_addr_table(self):
|
||||
async def get_mac_addr_table(self):
|
||||
"""
|
||||
Returns the MAC address table for this Ethernet switch.
|
||||
|
||||
:returns: list of entries (Ethernet address, VLAN, NIO)
|
||||
"""
|
||||
|
||||
mac_addr_table = yield from self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
|
||||
mac_addr_table = await self._hypervisor.send('ethsw show_mac_addr_table "{}"'.format(self._name))
|
||||
return mac_addr_table
|
||||
|
||||
@asyncio.coroutine
|
||||
def clear_mac_addr_table(self):
|
||||
async def clear_mac_addr_table(self):
|
||||
"""
|
||||
Clears the MAC address table for this Ethernet switch.
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
|
||||
await self._hypervisor.send('ethsw clear_mac_addr_table "{}"'.format(self._name))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -443,15 +428,14 @@ class EthernetSwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Ethernet switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -466,7 +450,7 @@ class EthernetSwitch(Device):
|
||||
if not nio:
|
||||
raise DynamipsError("Port {} is not connected".format(port_number))
|
||||
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Ethernet switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
@@ -63,26 +63,24 @@ class FrameRelaySwitch(Device):
|
||||
"mappings": mappings,
|
||||
"status": "started"}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
if self._hypervisor is None:
|
||||
module_workdir = self.project.module_working_directory(self.manager.module_name.lower())
|
||||
self._hypervisor = yield from self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
self._hypervisor = await self.manager.start_new_hypervisor(working_dir=module_workdir)
|
||||
|
||||
yield from self._hypervisor.send('frsw create "{}"'.format(self._name))
|
||||
await self._hypervisor.send('frsw create "{}"'.format(self._name))
|
||||
log.info('Frame Relay switch "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
|
||||
self._hypervisor.devices.append(self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_name(self, new_name):
|
||||
async def set_name(self, new_name):
|
||||
"""
|
||||
Renames this Frame Relay switch.
|
||||
|
||||
:param new_name: New name for this switch
|
||||
"""
|
||||
|
||||
yield from self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
await self._hypervisor.send('frsw rename "{name}" "{new_name}"'.format(name=self._name, new_name=new_name))
|
||||
log.info('Frame Relay switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
|
||||
id=self._id,
|
||||
new_name=new_name))
|
||||
@@ -118,15 +116,14 @@ class FrameRelaySwitch(Device):
|
||||
|
||||
self._mappings = mappings
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
for nio in self._nios.values():
|
||||
if nio:
|
||||
yield from nio.close()
|
||||
await nio.close()
|
||||
|
||||
if self._hypervisor:
|
||||
try:
|
||||
yield from self._hypervisor.send('frsw delete "{}"'.format(self._name))
|
||||
await self._hypervisor.send('frsw delete "{}"'.format(self._name))
|
||||
log.info('Frame Relay switch "{name}" [{id}] has been deleted'.format(name=self._name, id=self._id))
|
||||
except DynamipsError:
|
||||
log.debug("Could not properly delete Frame relay switch {}".format(self._name))
|
||||
@@ -134,15 +131,14 @@ class FrameRelaySwitch(Device):
|
||||
if self._hypervisor and self in self._hypervisor.devices:
|
||||
self._hypervisor.devices.remove(self)
|
||||
if self._hypervisor and not self._hypervisor.devices:
|
||||
yield from self.hypervisor.stop()
|
||||
await self.hypervisor.stop()
|
||||
self._hypervisor = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Deletes this Frame Relay switch.
|
||||
"""
|
||||
yield from self.close()
|
||||
await self.close()
|
||||
return True
|
||||
|
||||
def has_port(self, port):
|
||||
@@ -156,8 +152,7 @@ class FrameRelaySwitch(Device):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def add_nio(self, nio, port_number):
|
||||
async def add_nio(self, nio, port_number):
|
||||
"""
|
||||
Adds a NIO as new port on Frame Relay switch.
|
||||
|
||||
@@ -174,10 +169,9 @@ class FrameRelaySwitch(Device):
|
||||
port=port_number))
|
||||
|
||||
self._nios[port_number] = nio
|
||||
yield from self.set_mappings(self._mappings)
|
||||
await self.set_mappings(self._mappings)
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_nio(self, port_number):
|
||||
async def remove_nio(self, port_number):
|
||||
"""
|
||||
Removes the specified NIO as member of this Frame Relay switch.
|
||||
|
||||
@@ -200,8 +194,8 @@ class FrameRelaySwitch(Device):
|
||||
source_dlci=source_dlci,
|
||||
destination_port=destination_port,
|
||||
destination_dlci=destination_dlci))
|
||||
yield from self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
yield from self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
await self.unmap_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
await self.unmap_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
|
||||
nio = self._nios[port_number]
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -215,8 +209,7 @@ class FrameRelaySwitch(Device):
|
||||
del self._nios[port_number]
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_mappings(self, mappings):
|
||||
async def set_mappings(self, mappings):
|
||||
"""
|
||||
Applies VC mappings
|
||||
|
||||
@@ -237,11 +230,10 @@ class FrameRelaySwitch(Device):
|
||||
destination_port=destination_port,
|
||||
destination_dlci=destination_dlci))
|
||||
|
||||
yield from self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
yield from self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
await self.map_vc(source_port, source_dlci, destination_port, destination_dlci)
|
||||
await self.map_vc(destination_port, destination_dlci, source_port, source_dlci)
|
||||
|
||||
@asyncio.coroutine
|
||||
def map_vc(self, port1, dlci1, port2, dlci2):
|
||||
async def map_vc(self, port1, dlci1, port2, dlci2):
|
||||
"""
|
||||
Creates a new Virtual Circuit connection (unidirectional).
|
||||
|
||||
@@ -260,7 +252,7 @@ class FrameRelaySwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
await self._hypervisor.send('frsw create_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_dlci=dlci1,
|
||||
output_nio=nio2,
|
||||
@@ -275,8 +267,7 @@ class FrameRelaySwitch(Device):
|
||||
|
||||
self._active_mappings[(port1, dlci1)] = (port2, dlci2)
|
||||
|
||||
@asyncio.coroutine
|
||||
def unmap_vc(self, port1, dlci1, port2, dlci2):
|
||||
async def unmap_vc(self, port1, dlci1, port2, dlci2):
|
||||
"""
|
||||
Deletes a Virtual Circuit connection (unidirectional).
|
||||
|
||||
@@ -295,7 +286,7 @@ class FrameRelaySwitch(Device):
|
||||
nio1 = self._nios[port1]
|
||||
nio2 = self._nios[port2]
|
||||
|
||||
yield from self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
await self._hypervisor.send('frsw delete_vc "{name}" {input_nio} {input_dlci} {output_nio} {output_dlci}'.format(name=self._name,
|
||||
input_nio=nio1,
|
||||
input_dlci=dlci1,
|
||||
output_nio=nio2,
|
||||
@@ -309,8 +300,7 @@ class FrameRelaySwitch(Device):
|
||||
dlci2=dlci2))
|
||||
del self._active_mappings[(port1, dlci1)]
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
|
||||
async def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -331,15 +321,14 @@ class FrameRelaySwitch(Device):
|
||||
if nio.input_filter[0] is not None and nio.output_filter[0] is not None:
|
||||
raise DynamipsError("Port {} has already a filter applied".format(port_number))
|
||||
|
||||
yield from nio.bind_filter("both", "capture")
|
||||
yield from nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
await nio.bind_filter("both", "capture")
|
||||
await nio.setup_filter("both", '{} "{}"'.format(data_link_type, output_file))
|
||||
|
||||
log.info('Frame relay switch "{name}" [{id}]: starting packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -350,7 +339,7 @@ class FrameRelaySwitch(Device):
|
||||
raise DynamipsError("Port {} is not allocated".format(port_number))
|
||||
|
||||
nio = self._nios[port_number]
|
||||
yield from nio.unbind_filter("both")
|
||||
await nio.unbind_filter("both")
|
||||
log.info('Frame relay switch "{name}" [{id}]: stopping packet capture on port {port}'.format(name=self._name,
|
||||
id=self._id,
|
||||
port=port_number))
|
||||
File diff suppressed because it is too large.
Load diff
@@ -41,20 +41,19 @@ class IOU(BaseManager):
|
||||
super().__init__()
|
||||
self._iou_id_lock = asyncio.Lock()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new IOU VM.
|
||||
|
||||
:returns: IOUVM instance
|
||||
"""
|
||||
|
||||
with (yield from self._iou_id_lock):
|
||||
async with self._iou_id_lock:
|
||||
# wait for a node to be completely created before adding a new one
|
||||
# this is important otherwise we allocate the same application ID
|
||||
# when creating multiple IOU node at the same time
|
||||
application_id = get_next_application_id(self.nodes)
|
||||
node = yield from super().create_node(*args, application_id=application_id, **kwargs)
|
||||
node = await super().create_node(*args, application_id=application_id, **kwargs)
|
||||
return node
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -101,13 +101,12 @@ class IOUVM(BaseNode):
|
||||
self.save_configs()
|
||||
self.updated()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this IOU VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
adapters = self._ethernet_adapters + self._serial_adapters
|
||||
@@ -117,7 +116,7 @@ class IOUVM(BaseNode):
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
@@ -164,14 +163,13 @@ class IOUVM(BaseNode):
|
||||
else:
|
||||
log.info('IOU "{name}" [{id}]: does not use the default IOU image values'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def update_default_iou_values(self):
|
||||
async def update_default_iou_values(self):
|
||||
"""
|
||||
Finds the default RAM and NVRAM values for the IOU image.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, stderr=True)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, stderr=True)
|
||||
match = re.search("-n <n>\s+Size of nvram in Kb \(default ([0-9]+)KB\)", output)
|
||||
if match:
|
||||
self.nvram = int(match.group(1))
|
||||
@@ -181,10 +179,9 @@ class IOUVM(BaseNode):
|
||||
except (ValueError, OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("could not find default RAM and NVRAM values for {}: {}".format(os.path.basename(self._path), e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
|
||||
yield from self.update_default_iou_values()
|
||||
await self.update_default_iou_values()
|
||||
|
||||
def _check_requirements(self):
|
||||
"""
|
||||
@@ -361,14 +358,13 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Could not write the iourc file {}: {}".format(path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _library_check(self):
|
||||
async def _library_check(self):
|
||||
"""
|
||||
Checks for missing shared library dependencies in the IOU image.
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output("ldd", self._path)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output("ldd", self._path)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.warning("Could not determine the shared library dependencies for {}: {}".format(self._path, e))
|
||||
return
|
||||
@@ -379,8 +375,7 @@ class IOUVM(BaseNode):
|
||||
raise IOUError("The following shared library dependencies cannot be found for IOU image {}: {}".format(self._path,
|
||||
", ".join(missing_libs)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_iou_licence(self):
|
||||
async def _check_iou_licence(self):
|
||||
"""
|
||||
Checks for a valid IOU key in the iourc file (paranoid mode).
|
||||
"""
|
||||
@@ -419,7 +414,7 @@ class IOUVM(BaseNode):
|
||||
# in tests or generating one
|
||||
if not hasattr(sys, "_called_from_test"):
|
||||
try:
|
||||
hostid = (yield from gns3server.utils.asyncio.subprocess_check_output("hostid")).strip()
|
||||
hostid = (await gns3server.utils.asyncio.subprocess_check_output("hostid")).strip()
|
||||
except FileNotFoundError as e:
|
||||
raise IOUError("Could not find hostid: {}".format(e))
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
@@ -477,8 +472,7 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Cannot write nvram file {}: {}".format(nvram_file, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the IOU process.
|
||||
"""
|
||||
@@ -486,7 +480,7 @@ class IOUVM(BaseNode):
|
||||
self._check_requirements()
|
||||
if not self.is_running():
|
||||
|
||||
yield from self._library_check()
|
||||
await self._library_check()
|
||||
|
||||
try:
|
||||
self._rename_nvram_file()
|
||||
@@ -499,13 +493,13 @@ class IOUVM(BaseNode):
|
||||
if not os.path.isfile(iourc_path):
|
||||
raise IOUError("The iourc path '{}' is not a regular file".format(iourc_path))
|
||||
|
||||
yield from self._check_iou_licence()
|
||||
yield from self._start_ubridge()
|
||||
await self._check_iou_licence()
|
||||
await self._start_ubridge()
|
||||
|
||||
self._create_netmap_config()
|
||||
if self.use_default_iou_values:
|
||||
# make sure we have the default nvram amount to correctly push the configs
|
||||
yield from self.update_default_iou_values()
|
||||
await self.update_default_iou_values()
|
||||
self._push_configs_to_nvram()
|
||||
|
||||
# check if there is enough RAM to run
|
||||
@@ -518,11 +512,11 @@ class IOUVM(BaseNode):
|
||||
|
||||
if "IOURC" not in os.environ:
|
||||
env["IOURC"] = iourc_path
|
||||
command = yield from self._build_command()
|
||||
command = await self._build_command()
|
||||
try:
|
||||
log.info("Starting IOU: {}".format(command))
|
||||
self.command_line = ' '.join(command)
|
||||
self._iou_process = yield from asyncio.create_subprocess_exec(
|
||||
self._iou_process = await asyncio.create_subprocess_exec(
|
||||
*command,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stdin=asyncio.subprocess.PIPE,
|
||||
@@ -544,17 +538,16 @@ class IOUVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
raise IOUError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e))
|
||||
|
||||
# configure networking support
|
||||
yield from self._networking()
|
||||
await self._networking()
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _networking(self):
|
||||
async def _networking(self):
|
||||
"""
|
||||
Configures the IOL bridge in uBridge.
|
||||
"""
|
||||
@@ -562,10 +555,10 @@ class IOUVM(BaseNode):
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
try:
|
||||
# delete any previous bridge if it exists
|
||||
yield from self._ubridge_send("iol_bridge delete {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("iol_bridge delete {name}".format(name=bridge_name))
|
||||
except UbridgeError:
|
||||
pass
|
||||
yield from self._ubridge_send("iol_bridge create {name} {bridge_id}".format(name=bridge_name, bridge_id=self.application_id + 512))
|
||||
await self._ubridge_send("iol_bridge create {name} {bridge_id}".format(name=bridge_name, bridge_id=self.application_id + 512))
|
||||
|
||||
bay_id = 0
|
||||
for adapter in self._adapters:
|
||||
@@ -573,7 +566,7 @@ class IOUVM(BaseNode):
|
||||
for unit in adapter.ports.keys():
|
||||
nio = adapter.get_nio(unit)
|
||||
if nio and isinstance(nio, NIOUDP):
|
||||
yield from self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
iol_id=self.application_id,
|
||||
bay=bay_id,
|
||||
unit=unit_id,
|
||||
@@ -581,15 +574,15 @@ class IOUVM(BaseNode):
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('iol_bridge start_capture {name} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge start_capture {name} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
output_file=nio.pcap_output_file,
|
||||
data_link_type=re.sub("^DLT_", "", nio.pcap_data_link_type)))
|
||||
|
||||
yield from self._ubridge_apply_filters(bay_id, unit_id, nio.filters)
|
||||
await self._ubridge_apply_filters(bay_id, unit_id, nio.filters)
|
||||
unit_id += 1
|
||||
bay_id += 1
|
||||
|
||||
yield from self._ubridge_send("iol_bridge start {name}".format(name=bridge_name))
|
||||
await self._ubridge_send("iol_bridge start {name}".format(name=bridge_name))
|
||||
|
||||
def _termination_callback(self, process_name, returncode):
|
||||
"""
|
||||
@@ -624,13 +617,12 @@ class IOUVM(BaseNode):
|
||||
for file_path in glob.glob(os.path.join(glob.escape(self.working_dir), "vlan.dat-*")):
|
||||
shutil.move(file_path, destination)
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the IOU process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self._nvram_watcher:
|
||||
self._nvram_watcher.close()
|
||||
self._nvram_watcher = None
|
||||
@@ -643,7 +635,7 @@ class IOUVM(BaseNode):
|
||||
self._terminate_process_iou()
|
||||
if self._iou_process.returncode is None:
|
||||
try:
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._iou_process, timeout=3)
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._iou_process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._iou_process.returncode is None:
|
||||
log.warning("IOU process {} is still running... killing it".format(self._iou_process.pid))
|
||||
@@ -671,14 +663,13 @@ class IOUVM(BaseNode):
|
||||
self._started = False
|
||||
self.status = "stopped"
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the IOU process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start()
|
||||
await self.stop()
|
||||
await self.start()
|
||||
|
||||
def is_running(self):
|
||||
"""
|
||||
@@ -723,8 +714,7 @@ class IOUVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise IOUError("Could not create {}: {}".format(netmap_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _build_command(self):
|
||||
async def _build_command(self):
|
||||
"""
|
||||
Command to start the IOU process.
|
||||
(to be passed to subprocess.Popen())
|
||||
@@ -769,7 +759,7 @@ class IOUVM(BaseNode):
|
||||
# command.extend(["-c", os.path.basename(startup_config_file)])
|
||||
|
||||
if self._l1_keepalives:
|
||||
yield from self._enable_l1_keepalives(command)
|
||||
await self._enable_l1_keepalives(command)
|
||||
command.extend([str(self.application_id)])
|
||||
return command
|
||||
|
||||
@@ -848,8 +838,7 @@ class IOUVM(BaseNode):
|
||||
|
||||
self._adapters = self._ethernet_adapters + self._serial_adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, port_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, port_number, nio):
|
||||
"""
|
||||
Adds a adapter NIO binding.
|
||||
|
||||
@@ -877,17 +866,16 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(name=bridge_name,
|
||||
iol_id=self.application_id,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
await self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, port_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, port_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@@ -897,10 +885,9 @@ class IOUVM(BaseNode):
|
||||
"""
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
await self._ubridge_apply_filters(adapter_number, port_number, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _ubridge_apply_filters(self, adapter_number, port_number, filters):
|
||||
async def _ubridge_apply_filters(self, adapter_number, port_number, filters):
|
||||
"""
|
||||
Apply filter like rate limiting
|
||||
|
||||
@@ -913,15 +900,14 @@ class IOUVM(BaseNode):
|
||||
bridge_name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number)
|
||||
yield from self._ubridge_send('iol_bridge reset_packet_filters ' + location)
|
||||
await self._ubridge_send('iol_bridge reset_packet_filters ' + location)
|
||||
for filter in self._build_filter_list(filters):
|
||||
cmd = 'iol_bridge add_packet_filter {} {}'.format(
|
||||
location,
|
||||
filter)
|
||||
yield from self._ubridge_send(cmd)
|
||||
await self._ubridge_send(cmd)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@@ -952,7 +938,7 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send("iol_bridge delete_nio_udp {name} {bay} {unit}".format(name=bridge_name,
|
||||
await self._ubridge_send("iol_bridge delete_nio_udp {name} {bay} {unit}".format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number))
|
||||
|
||||
@@ -982,8 +968,7 @@ class IOUVM(BaseNode):
|
||||
else:
|
||||
log.info('IOU "{name}" [{id}]: has deactivated layer 1 keepalive messages'.format(name=self._name, id=self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _enable_l1_keepalives(self, command):
|
||||
async def _enable_l1_keepalives(self, command):
|
||||
"""
|
||||
Enables L1 keepalive messages if supported.
|
||||
|
||||
@@ -994,7 +979,7 @@ class IOUVM(BaseNode):
|
||||
if "IOURC" not in os.environ:
|
||||
env["IOURC"] = self.iourc_path
|
||||
try:
|
||||
output = yield from gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, env=env, stderr=True)
|
||||
output = await gns3server.utils.asyncio.subprocess_check_output(self._path, "-h", cwd=self.working_dir, env=env, stderr=True)
|
||||
if re.search("-l\s+Enable Layer 1 keepalive messages", output):
|
||||
command.extend(["-l"])
|
||||
else:
|
||||
@@ -1226,8 +1211,7 @@ class IOUVM(BaseNode):
|
||||
except (binascii.Error, OSError) as e:
|
||||
raise IOUError("Could not save the private configuration {}: {}".format(config_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
async def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -1265,14 +1249,13 @@ class IOUVM(BaseNode):
|
||||
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send('iol_bridge start_capture {name} {bay} {unit} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge start_capture {name} {bay} {unit} "{output_file}" {data_link_type}'.format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
output_file=output_file,
|
||||
data_link_type=re.sub("^DLT_", "", data_link_type)))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, adapter_number, port_number):
|
||||
async def stop_capture(self, adapter_number, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -1302,6 +1285,6 @@ class IOUVM(BaseNode):
|
||||
port_number=port_number))
|
||||
if self.ubridge:
|
||||
bridge_name = "IOL-BRIDGE-{}".format(self.application_id + 512)
|
||||
yield from self._ubridge_send('iol_bridge stop_capture {name} {bay} {unit}'.format(name=bridge_name,
|
||||
await self._ubridge_send('iol_bridge stop_capture {name} {bay} {unit}'.format(name=bridge_name,
|
||||
bay=adapter_number,
|
||||
unit=port_number))
|
||||
@@ -26,7 +26,7 @@ from uuid import UUID, uuid4
|
||||
from .port_manager import PortManager
|
||||
from .notification_manager import NotificationManager
|
||||
from ..config import Config
|
||||
from ..utils.asyncio import wait_run_in_executor, asyncio_ensure_future
|
||||
from ..utils.asyncio import wait_run_in_executor
|
||||
from ..utils.path import check_path_allowed, get_default_project_directory
|
||||
|
||||
import logging
|
||||
@@ -282,7 +282,7 @@ class Project:
|
||||
|
||||
raise aiohttp.web.HTTPNotFound(text="Node ID {} doesn't exist".format(node_id))
|
||||
|
||||
def remove_node(self, node):
|
||||
async def remove_node(self, node):
|
||||
"""
|
||||
Removes a node from the project.
|
||||
In theory this should be called by the node manager.
|
||||
@@ -291,11 +291,10 @@ class Project:
|
||||
"""
|
||||
|
||||
if node in self._nodes:
|
||||
yield from node.delete()
|
||||
await node.delete()
|
||||
self._nodes.remove(node)
|
||||
|
||||
@asyncio.coroutine
|
||||
def update(self, variables=None, **kwargs):
|
||||
async def update(self, variables=None, **kwargs):
|
||||
original_variables = self.variables
|
||||
self.variables = variables
|
||||
|
||||
@@ -303,10 +302,9 @@ class Project:
|
||||
if original_variables != variables:
|
||||
for node in self.nodes:
|
||||
if hasattr(node, 'update'):
|
||||
yield from node.update()
|
||||
await node.update()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes the project, but keep project data on disk
|
||||
"""
|
||||
@@ -317,15 +315,15 @@ class Project:
|
||||
module_nodes_id = set([n.id for n in module.instance().nodes])
|
||||
# We close the project only for the modules using it
|
||||
if len(module_nodes_id & project_nodes_id):
|
||||
yield from module.instance().project_closing(self)
|
||||
await module.instance().project_closing(self)
|
||||
|
||||
yield from self._close_and_clean(False)
|
||||
await self._close_and_clean(False)
|
||||
|
||||
for module in self.compute():
|
||||
module_nodes_id = set([n.id for n in module.instance().nodes])
|
||||
# We close the project only for the modules using it
|
||||
if len(module_nodes_id & project_nodes_id):
|
||||
yield from module.instance().project_closed(self)
|
||||
await module.instance().project_closed(self)
|
||||
|
||||
try:
|
||||
if os.path.exists(self.tmp_working_directory()):
|
||||
@@ -333,8 +331,7 @@ class Project:
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
@asyncio.coroutine
|
||||
def _close_and_clean(self, cleanup):
|
||||
async def _close_and_clean(self, cleanup):
|
||||
"""
|
||||
Closes the project, and cleanup the disk if cleanup is True
|
||||
|
||||
@@ -343,10 +340,10 @@ class Project:
|
||||
|
||||
tasks = []
|
||||
for node in self._nodes:
|
||||
tasks.append(asyncio_ensure_future(node.manager.close_node(node.id)))
|
||||
tasks.append(asyncio.ensure_future(node.manager.close_node(node.id)))
|
||||
|
||||
if tasks:
|
||||
done, _ = yield from asyncio.wait(tasks)
|
||||
done, _ = await asyncio.wait(tasks)
|
||||
for future in done:
|
||||
try:
|
||||
future.result()
|
||||
@@ -356,7 +353,7 @@ class Project:
|
||||
if cleanup and os.path.exists(self.path):
|
||||
self._deleted = True
|
||||
try:
|
||||
yield from wait_run_in_executor(shutil.rmtree, self.path)
|
||||
await wait_run_in_executor(shutil.rmtree, self.path)
|
||||
log.info("Project {id} with path '{path}' deleted".format(path=self._path, id=self._id))
|
||||
except OSError as e:
|
||||
raise aiohttp.web.HTTPInternalServerError(text="Could not delete the project directory: {}".format(e))
|
||||
@@ -375,17 +372,16 @@ class Project:
|
||||
for port in self._used_udp_ports.copy():
|
||||
port_manager.release_udp_port(port, self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
async def delete(self):
|
||||
"""
|
||||
Removes project from disk
|
||||
"""
|
||||
|
||||
for module in self.compute():
|
||||
yield from module.instance().project_closing(self)
|
||||
yield from self._close_and_clean(True)
|
||||
await module.instance().project_closing(self)
|
||||
await self._close_and_clean(True)
|
||||
for module in self.compute():
|
||||
yield from module.instance().project_closed(self)
|
||||
await module.instance().project_closed(self)
|
||||
|
||||
def compute(self):
|
||||
"""
|
||||
@@ -405,8 +401,7 @@ class Project:
|
||||
"""
|
||||
NotificationManager.instance().emit(action, event, project_id=self.id)
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_files(self):
|
||||
async def list_files(self):
|
||||
"""
|
||||
:returns: Array of files in project without temporary files. The files are dictionary {"path": "test.bin", "md5sum": "aaaaa"}
|
||||
"""
|
||||
@@ -421,7 +416,7 @@ class Project:
|
||||
file_info = {"path": path}
|
||||
|
||||
try:
|
||||
file_info["md5sum"] = yield from wait_run_in_executor(self._hash_file, os.path.join(dirpath, filename))
|
||||
file_info["md5sum"] = await wait_run_in_executor(self._hash_file, os.path.join(dirpath, filename))
|
||||
except OSError:
|
||||
continue
|
||||
files.append(file_info)
|
||||
|
||||
@@ -41,8 +41,7 @@ class Qemu(BaseManager):
|
||||
_NODE_TYPE = "qemu"
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def get_kvm_archs():
|
||||
async def get_kvm_archs():
|
||||
"""
|
||||
Gets a list of architectures for which KVM is available on this server.
|
||||
|
||||
@@ -108,7 +107,7 @@ class Qemu(BaseManager):
|
||||
return paths
|
||||
|
||||
@staticmethod
|
||||
def binary_list(archs=None):
|
||||
async def binary_list(archs=None):
|
||||
"""
|
||||
Gets QEMU binaries list available on the host.
|
||||
|
||||
@@ -128,11 +127,11 @@ class Qemu(BaseManager):
|
||||
for arch in archs:
|
||||
if f.endswith(arch) or f.endswith("{}.exe".format(arch)) or f.endswith("{}w.exe".format(arch)):
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu.get_qemu_version(qemu_path)
|
||||
version = await Qemu.get_qemu_version(qemu_path)
|
||||
qemus.append({"path": qemu_path, "version": version})
|
||||
else:
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu.get_qemu_version(qemu_path)
|
||||
version = await Qemu.get_qemu_version(qemu_path)
|
||||
qemus.append({"path": qemu_path, "version": version})
|
||||
|
||||
except OSError:
|
||||
@@ -141,7 +140,7 @@ class Qemu(BaseManager):
|
||||
return qemus
|
||||
|
||||
@staticmethod
|
||||
def img_binary_list():
|
||||
async def img_binary_list():
|
||||
"""
|
||||
Gets QEMU-img binaries list available on the host.
|
||||
|
||||
@@ -155,7 +154,7 @@ class Qemu(BaseManager):
|
||||
os.access(os.path.join(path, f), os.X_OK) and \
|
||||
os.path.isfile(os.path.join(path, f)):
|
||||
qemu_path = os.path.join(path, f)
|
||||
version = yield from Qemu._get_qemu_img_version(qemu_path)
|
||||
version = await Qemu._get_qemu_img_version(qemu_path)
|
||||
qemu_imgs.append({"path": qemu_path, "version": version})
|
||||
except OSError:
|
||||
continue
|
||||
@@ -163,8 +162,7 @@ class Qemu(BaseManager):
|
||||
return qemu_imgs
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def get_qemu_version(qemu_path):
|
||||
async def get_qemu_version(qemu_path):
|
||||
"""
|
||||
Gets the Qemu version.
|
||||
|
||||
@@ -187,7 +185,7 @@ class Qemu(BaseManager):
|
||||
return ""
|
||||
else:
|
||||
try:
|
||||
output = yield from subprocess_check_output(qemu_path, "-version")
|
||||
output = await subprocess_check_output(qemu_path, "-version")
|
||||
match = re.search("version\s+([0-9a-z\-\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@@ -198,8 +196,7 @@ class Qemu(BaseManager):
|
||||
raise QemuError("Error while looking for the Qemu version: {}".format(e))
|
||||
|
||||
@staticmethod
|
||||
@asyncio.coroutine
|
||||
def _get_qemu_img_version(qemu_img_path):
|
||||
async def _get_qemu_img_version(qemu_img_path):
|
||||
"""
|
||||
Gets the Qemu-img version.
|
||||
|
||||
@@ -207,7 +204,7 @@ class Qemu(BaseManager):
|
||||
"""
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(qemu_img_path, "--version")
|
||||
output = await subprocess_check_output(qemu_img_path, "--version")
|
||||
match = re.search("version\s+([0-9a-z\-\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@@ -258,8 +255,7 @@ class Qemu(BaseManager):
|
||||
|
||||
return os.path.join("qemu", "vm-{}".format(legacy_vm_id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_disk(self, qemu_img, path, options):
|
||||
async def create_disk(self, qemu_img, path, options):
|
||||
"""
|
||||
Create a Qemu disk with qemu-img
|
||||
|
||||
@@ -290,13 +286,12 @@ class Qemu(BaseManager):
|
||||
command.append(path)
|
||||
command.append("{}M".format(img_size))
|
||||
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
await process.wait()
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not create disk image {}:{}".format(path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resize_disk(self, qemu_img, path, extend):
|
||||
async def resize_disk(self, qemu_img, path, extend):
|
||||
"""
|
||||
Resize a Qemu disk with qemu-img
|
||||
|
||||
@@ -314,8 +309,8 @@ class Qemu(BaseManager):
|
||||
if not os.path.exists(path):
|
||||
raise QemuError("Qemu disk '{}' does not exist".format(path))
|
||||
command = [qemu_img, "resize", path, "+{}M".format(extend)]
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
await process.wait()
|
||||
log.info("Qemu disk '{}' extended by {} MB".format(path, extend))
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not update disk image {}:{}".format(path, e))
|
||||
@@ -87,8 +87,7 @@ class Qcow2:
|
||||
return None
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def rebase(self, qemu_img, base_image):
|
||||
async def rebase(self, qemu_img, base_image):
|
||||
"""
|
||||
Rebase a linked clone in order to use the correct disk
|
||||
|
||||
@@ -99,8 +98,8 @@ class Qcow2:
|
||||
if not os.path.exists(base_image):
|
||||
raise FileNotFoundError(base_image)
|
||||
command = [qemu_img, "rebase", "-u", "-b", base_image, self._path]
|
||||
process = yield from asyncio.create_subprocess_exec(*command)
|
||||
retcode = yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command)
|
||||
retcode = await process.wait()
|
||||
if retcode != 0:
|
||||
raise Qcow2Error("Could not rebase the image")
|
||||
self._reload()
|
||||
@@ -795,8 +795,7 @@ class QemuVM(BaseNode):
|
||||
kernel_command_line=kernel_command_line))
|
||||
self._kernel_command_line = kernel_command_line
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_process_priority(self):
|
||||
async def _set_process_priority(self):
|
||||
"""
|
||||
Changes the process priority
|
||||
"""
|
||||
@@ -844,8 +843,8 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
priority = 0
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec('renice', '-n', str(priority), '-p', str(self._process.pid))
|
||||
await process.wait()
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
log.error('Could not change process priority for QEMU VM "{}": {}'.format(self._name, e))
|
||||
|
||||
@@ -881,8 +880,7 @@ class QemuVM(BaseNode):
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise QemuError("Could not throttle CPU: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates QEMU VM and sets proper MD5 hashes
|
||||
"""
|
||||
@@ -890,23 +888,22 @@ class QemuVM(BaseNode):
|
||||
# In case user upload image manually we don't have md5 sums.
|
||||
# We need generate hashes at this point, otherwise they will be generated
|
||||
# at __json__ but not on separate thread.
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hda_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdb_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdc_disk_image)
|
||||
yield from cancellable_wait_run_in_executor(md5sum, self._hdd_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hda_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdb_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdc_disk_image)
|
||||
await cancellable_wait_run_in_executor(md5sum, self._hdd_disk_image)
|
||||
|
||||
super(QemuVM, self).create()
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this QEMU VM.
|
||||
"""
|
||||
|
||||
with (yield from self._execute_lock):
|
||||
async with self._execute_lock:
|
||||
if self.is_running():
|
||||
# resume the VM if it is paused
|
||||
yield from self.resume()
|
||||
await self.resume()
|
||||
return
|
||||
|
||||
if self._manager.config.get_section_config("Qemu").getboolean("monitor", True):
|
||||
@@ -927,7 +924,7 @@ class QemuVM(BaseNode):
|
||||
# check if there is enough RAM to run
|
||||
self.check_available_ram(self.ram)
|
||||
|
||||
command = yield from self._build_command()
|
||||
command = await self._build_command()
|
||||
command_string = " ".join(shlex.quote(s) for s in command)
|
||||
try:
|
||||
log.info("Starting QEMU with: {}".format(command_string))
|
||||
@@ -936,7 +933,7 @@ class QemuVM(BaseNode):
|
||||
with open(self._stdout_file, "w", encoding="utf-8") as fd:
|
||||
fd.write("Start QEMU with {}\n\nExecution log:\n".format(command_string))
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self.working_dir)
|
||||
@@ -948,34 +945,33 @@ class QemuVM(BaseNode):
|
||||
log.error("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
|
||||
raise QemuError("Could not start QEMU {}: {}\n{}".format(self.qemu_path, e, stdout))
|
||||
|
||||
yield from self._set_process_priority()
|
||||
await self._set_process_priority()
|
||||
if self._cpu_throttling:
|
||||
self._set_cpu_throttling()
|
||||
|
||||
if "-enable-kvm" in command_string or "-enable-hax" in command_string:
|
||||
self._hw_virtualization = True
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
set_link_commands = []
|
||||
for adapter_number, adapter in enumerate(self._ethernet_adapters):
|
||||
nio = adapter.get_nio(0)
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
set_link_commands.append("set_link gns3-{} off".format(adapter_number))
|
||||
else:
|
||||
set_link_commands.append("set_link gns3-{} off".format(adapter_number))
|
||||
yield from self._control_vm_commands(set_link_commands)
|
||||
await self._control_vm_commands(set_link_commands)
|
||||
|
||||
try:
|
||||
yield from self.start_wrap_console()
|
||||
await self.start_wrap_console()
|
||||
except OSError as e:
|
||||
raise QemuError("Could not start Telnet QEMU console {}\n".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _termination_callback(self, returncode):
|
||||
async def _termination_callback(self, returncode):
|
||||
"""
|
||||
Called when the process has stopped.
|
||||
|
||||
@@ -984,19 +980,18 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.started:
|
||||
log.info("QEMU process has stopped, return code: %d", returncode)
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
# A return code of 1 seem fine on Windows
|
||||
if returncode != 0 and (returncode != 1 or not sys.platform.startswith("win")):
|
||||
self.project.emit("log.error", {"message": "QEMU process has stopped, return code: {}\n{}".format(returncode, self.read_stdout())})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this QEMU VM.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
with (yield from self._execute_lock):
|
||||
await self._stop_ubridge()
|
||||
async with self._execute_lock:
|
||||
# stop the QEMU process
|
||||
self._hw_virtualization = False
|
||||
if self.is_running():
|
||||
@@ -1004,22 +999,22 @@ class QemuVM(BaseNode):
|
||||
try:
|
||||
|
||||
if self.on_close == "save_vm_state":
|
||||
yield from self._control_vm("stop")
|
||||
yield from self._control_vm("savevm GNS3_SAVED_STATE")
|
||||
await self._control_vm("stop")
|
||||
await self._control_vm("savevm GNS3_SAVED_STATE")
|
||||
wait_for_savevm = 120
|
||||
while wait_for_savevm:
|
||||
yield from asyncio.sleep(1)
|
||||
status = yield from self._saved_state_option()
|
||||
await asyncio.sleep(1)
|
||||
status = await self._saved_state_option()
|
||||
wait_for_savevm -= 1
|
||||
if status != []:
|
||||
break
|
||||
|
||||
if self.on_close == "shutdown_signal":
|
||||
yield from self._control_vm("system_powerdown")
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30)
|
||||
await self._control_vm("system_powerdown")
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=30)
|
||||
else:
|
||||
self._process.terminate()
|
||||
yield from gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
|
||||
await gns3server.utils.asyncio.wait_for_process_termination(self._process, timeout=3)
|
||||
except ProcessLookupError:
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
@@ -1033,11 +1028,10 @@ class QemuVM(BaseNode):
|
||||
self._process = None
|
||||
self._stop_cpulimit()
|
||||
if self.on_close != "save_vm_state":
|
||||
yield from self._clear_save_vm_stated()
|
||||
yield from super().stop()
|
||||
await self._clear_save_vm_stated()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _open_qemu_monitor_connection_vm(self, timeout=10):
|
||||
async def _open_qemu_monitor_connection_vm(self, timeout=10):
|
||||
"""
|
||||
Opens a connection to the QEMU monitor.
|
||||
|
||||
@@ -1050,10 +1044,10 @@ class QemuVM(BaseNode):
|
||||
last_exception = None
|
||||
reader = writer = None
|
||||
while time.time() - begin < timeout:
|
||||
yield from asyncio.sleep(0.01)
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
log.debug("Connecting to Qemu monitor on {}:{}".format(self._monitor_host, self._monitor))
|
||||
reader, writer = yield from asyncio.open_connection(self._monitor_host, self._monitor)
|
||||
reader, writer = await asyncio.open_connection(self._monitor_host, self._monitor)
|
||||
except (asyncio.TimeoutError, OSError) as e:
|
||||
last_exception = e
|
||||
continue
|
||||
@@ -1067,8 +1061,7 @@ class QemuVM(BaseNode):
|
||||
log.info("Connected to QEMU monitor on {}:{} after {:.4f} seconds".format(self._monitor_host, self._monitor, time.time() - begin))
|
||||
return reader, writer
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, command, expected=None):
|
||||
async def _control_vm(self, command, expected=None):
|
||||
"""
|
||||
Executes a command with QEMU monitor when this VM is running.
|
||||
|
||||
@@ -1081,7 +1074,7 @@ class QemuVM(BaseNode):
|
||||
result = None
|
||||
if self.is_running() and self._monitor:
|
||||
log.info("Execute QEMU monitor command: {}".format(command))
|
||||
reader, writer = yield from self._open_qemu_monitor_connection_vm()
|
||||
reader, writer = await self._open_qemu_monitor_connection_vm()
|
||||
if reader is None and writer is None:
|
||||
return result
|
||||
|
||||
@@ -1094,7 +1087,7 @@ class QemuVM(BaseNode):
|
||||
if expected:
|
||||
try:
|
||||
while result is None:
|
||||
line = yield from reader.readline()
|
||||
line = await reader.readline()
|
||||
if not line:
|
||||
break
|
||||
for expect in expected:
|
||||
@@ -1106,8 +1099,7 @@ class QemuVM(BaseNode):
|
||||
writer.close()
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm_commands(self, commands):
|
||||
async def _control_vm_commands(self, commands):
|
||||
"""
|
||||
Executes commands with QEMU monitor when this VM is running.
|
||||
|
||||
@@ -1116,7 +1108,7 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running() and self._monitor:
|
||||
|
||||
reader, writer = yield from self._open_qemu_monitor_connection_vm()
|
||||
reader, writer = await self._open_qemu_monitor_connection_vm()
|
||||
if reader is None and writer is None:
|
||||
return
|
||||
|
||||
@@ -1128,17 +1120,16 @@ class QemuVM(BaseNode):
|
||||
log.warning("Could not write to QEMU monitor: {}".format(e))
|
||||
writer.close()
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this QEMU VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
for adapter in self._ethernet_adapters:
|
||||
if adapter is not None:
|
||||
@@ -1151,8 +1142,7 @@ class QemuVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnels = {}
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_status(self):
|
||||
async def _get_vm_status(self):
|
||||
"""
|
||||
Returns this VM suspend status.
|
||||
|
||||
@@ -1162,7 +1152,7 @@ class QemuVM(BaseNode):
|
||||
:returns: status (string)
|
||||
"""
|
||||
|
||||
result = yield from self._control_vm("info status", [
|
||||
result = await self._control_vm("info status", [
|
||||
b"debug", b"inmigrate", b"internal-error", b"io-error",
|
||||
b"paused", b"postmigrate", b"prelaunch", b"finish-migrate",
|
||||
b"restore-vm", b"running", b"save-vm", b"shutdown", b"suspended",
|
||||
@@ -1179,49 +1169,45 @@ class QemuVM(BaseNode):
|
||||
self.status = "stopped"
|
||||
return status
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this QEMU VM.
|
||||
"""
|
||||
|
||||
if self.is_running():
|
||||
vm_status = yield from self._get_vm_status()
|
||||
vm_status = await self._get_vm_status()
|
||||
if vm_status is None:
|
||||
raise QemuError("Suspending a QEMU VM is not supported")
|
||||
elif vm_status == "running" or vm_status == "prelaunch":
|
||||
yield from self._control_vm("stop")
|
||||
await self._control_vm("stop")
|
||||
self.status = "suspended"
|
||||
log.debug("QEMU VM has been suspended")
|
||||
else:
|
||||
log.info("QEMU VM is not running to be suspended, current status is {}".format(vm_status))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this QEMU VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("system_reset")
|
||||
await self._control_vm("system_reset")
|
||||
log.debug("QEMU VM has been reset")
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this QEMU VM.
|
||||
"""
|
||||
|
||||
vm_status = yield from self._get_vm_status()
|
||||
vm_status = await self._get_vm_status()
|
||||
if vm_status is None:
|
||||
raise QemuError("Resuming a QEMU VM is not supported")
|
||||
elif vm_status == "paused":
|
||||
yield from self._control_vm("cont")
|
||||
await self._control_vm("cont")
|
||||
log.debug("QEMU VM has been resumed")
|
||||
else:
|
||||
log.info("QEMU VM is not paused to be resumed, current status is {}".format(vm_status))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@@ -1237,10 +1223,10 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
yield from self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
except (IndexError, KeyError):
|
||||
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
|
||||
adapter_number=adapter_number))
|
||||
@@ -1251,8 +1237,7 @@ class QemuVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@@ -1262,19 +1247,18 @@ class QemuVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.update_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self.update_ubridge_udp_connection("QEMU-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
yield from self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
else:
|
||||
yield from self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
await self._control_vm("set_link gns3-{} on".format(adapter_number))
|
||||
except IndexError:
|
||||
raise QemuError('Adapter {adapter_number} does not exist on QEMU VM "{name}"'.format(name=self._name,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@@ -1290,8 +1274,8 @@ class QemuVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
await self._control_vm("set_link gns3-{} off".format(adapter_number))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -1304,8 +1288,7 @@ class QemuVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -1330,14 +1313,14 @@ class QemuVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="QEMU-{}-{}".format(self._id, adapter_number),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="QEMU-{}-{}".format(self._id, adapter_number),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("QEMU VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -1357,7 +1340,7 @@ class QemuVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="QEMU-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
log.info("QEMU VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
@@ -1511,21 +1494,19 @@ class QemuVM(BaseNode):
|
||||
|
||||
return qemu_img_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def _qemu_img_exec(self, command):
|
||||
async def _qemu_img_exec(self, command):
|
||||
|
||||
self._qemu_img_stdout_file = os.path.join(self.working_dir, "qemu-img.log")
|
||||
log.info("logging to {}".format(self._qemu_img_stdout_file))
|
||||
command_string = " ".join(shlex.quote(s) for s in command)
|
||||
log.info("Executing qemu-img with: {}".format(command_string))
|
||||
with open(self._qemu_img_stdout_file, "w", encoding="utf-8") as fd:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir)
|
||||
retcode = yield from process.wait()
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=fd, stderr=subprocess.STDOUT, cwd=self.working_dir)
|
||||
retcode = await process.wait()
|
||||
log.info("{} returned with {}".format(self._get_qemu_img(), retcode))
|
||||
return retcode
|
||||
|
||||
@asyncio.coroutine
|
||||
def _disk_options(self):
|
||||
async def _disk_options(self):
|
||||
options = []
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
|
||||
@@ -1548,16 +1529,16 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
try:
|
||||
# check for corrupt disk image
|
||||
retcode = yield from self._qemu_img_exec([qemu_img_path, "check", disk_image])
|
||||
retcode = await self._qemu_img_exec([qemu_img_path, "check", disk_image])
|
||||
if retcode == 3:
|
||||
# image has leaked clusters, but is not corrupted, let's try to fix it
|
||||
log.warning("Qemu image {} has leaked clusters".format(disk_image))
|
||||
if (yield from self._qemu_img_exec([qemu_img_path, "check", "-r", "leaks", "{}".format(disk_image)])) == 3:
|
||||
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "leaks", "{}".format(disk_image)])) == 3:
|
||||
self.project.emit("log.warning", {"message": "Qemu image '{}' has leaked clusters and could not be fixed".format(disk_image)})
|
||||
elif retcode == 2:
|
||||
# image is corrupted, let's try to fix it
|
||||
log.warning("Qemu image {} is corrupted".format(disk_image))
|
||||
if (yield from self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
||||
if (await self._qemu_img_exec([qemu_img_path, "check", "-r", "all", "{}".format(disk_image)])) == 2:
|
||||
self.project.emit("log.warning", {"message": "Qemu image '{}' is corrupted and could not be fixed".format(disk_image)})
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
@@ -1569,7 +1550,7 @@ class QemuVM(BaseNode):
|
||||
# create the disk
|
||||
try:
|
||||
command = [qemu_img_path, "create", "-o", "backing_file={}".format(disk_image), "-f", "qcow2", disk]
|
||||
retcode = yield from self._qemu_img_exec(command)
|
||||
retcode = await self._qemu_img_exec(command)
|
||||
if retcode:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
raise QemuError("Could not create '{}' disk image: qemu-img returned with {}\n{}".format(disk_name,
|
||||
@@ -1582,7 +1563,7 @@ class QemuVM(BaseNode):
|
||||
# The disk exists we check if the clone works
|
||||
try:
|
||||
qcow2 = Qcow2(disk)
|
||||
yield from qcow2.rebase(qemu_img_path, disk_image)
|
||||
await qcow2.rebase(qemu_img_path, disk_image)
|
||||
except (Qcow2Error, OSError) as e:
|
||||
raise QemuError("Could not use qcow2 disk image '{}' for {} {}".format(disk_image, disk_name, e))
|
||||
|
||||
@@ -1599,8 +1580,7 @@ class QemuVM(BaseNode):
|
||||
|
||||
return options
|
||||
|
||||
@asyncio.coroutine
|
||||
def resize_disk(self, drive_name, extend):
|
||||
async def resize_disk(self, drive_name, extend):
|
||||
|
||||
if self.is_running():
|
||||
raise QemuError("Cannot resize {} while the VM is running".format(drive_name))
|
||||
@@ -1613,7 +1593,7 @@ class QemuVM(BaseNode):
|
||||
if not os.path.exists(disk_image_path):
|
||||
raise QemuError("Disk path '{}' does not exist".format(disk_image_path))
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
yield from self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
|
||||
await self.manager.resize_disk(qemu_img_path, disk_image_path, extend)
|
||||
|
||||
def _cdrom_option(self):
|
||||
|
||||
@@ -1663,15 +1643,14 @@ class QemuVM(BaseNode):
|
||||
|
||||
return options
|
||||
|
||||
@asyncio.coroutine
|
||||
def _network_options(self):
|
||||
async def _network_options(self):
|
||||
|
||||
network_options = []
|
||||
network_options.extend(["-net", "none"]) # we do not want any user networking back-end if no adapter is connected.
|
||||
|
||||
patched_qemu = False
|
||||
if self._legacy_networking:
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if version and parse_version(version) < parse_version("1.1.0"):
|
||||
# this is a patched Qemu if version is below 1.1.0
|
||||
patched_qemu = True
|
||||
@@ -1684,7 +1663,7 @@ class QemuVM(BaseNode):
|
||||
network_options.extend(["-device", "pci-bridge,id=pci-bridge{bridge_id},bus=dmi_pci_bridge{bridge_id},chassis_nr=0x1,addr=0x{bridge_id},shpc=off".format(bridge_id=bridge_id)])
|
||||
|
||||
if bridge_id > 1:
|
||||
qemu_version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
qemu_version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if qemu_version and parse_version(qemu_version) < parse_version("2.4.0"):
|
||||
raise QemuError("Qemu version 2.4 or later is required to run this VM with a large number of network adapters")
|
||||
|
||||
@@ -1765,8 +1744,7 @@ class QemuVM(BaseNode):
|
||||
return ["-nographic"]
|
||||
return []
|
||||
|
||||
@asyncio.coroutine
|
||||
def _run_with_hardware_acceleration(self, qemu_path, options):
|
||||
async def _run_with_hardware_acceleration(self, qemu_path, options):
|
||||
"""
|
||||
Check if we can run Qemu with hardware acceleration
|
||||
|
||||
@@ -1806,7 +1784,7 @@ class QemuVM(BaseNode):
|
||||
elif sys.platform.startswith("win"):
|
||||
if require_hardware_accel:
|
||||
# HAXM is only available starting with Qemu version 2.9.0
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
if version and parse_version(version) < parse_version("2.9.0"):
|
||||
raise QemuError("HAXM acceleration can only be enable for Qemu version 2.9.0 and above (current version: {})".format(version))
|
||||
|
||||
@@ -1824,8 +1802,8 @@ class QemuVM(BaseNode):
|
||||
else:
|
||||
return False
|
||||
elif sys.platform.startswith("darwin"):
|
||||
process = yield from asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
|
||||
yield from process.wait()
|
||||
process = await asyncio.create_subprocess_shell("kextstat | grep com.intel.kext.intelhaxm")
|
||||
await process.wait()
|
||||
if process.returncode != 0:
|
||||
if require_hardware_accel:
|
||||
raise QemuError("HAXM acceleration support is not installed on this host (com.intel.kext.intelhaxm extension not loaded)")
|
||||
@@ -1834,8 +1812,7 @@ class QemuVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _clear_save_vm_stated(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
async def _clear_save_vm_stated(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
|
||||
drives = ["a", "b", "c", "d"]
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
@@ -1851,7 +1828,7 @@ class QemuVM(BaseNode):
|
||||
if not os.path.exists(disk):
|
||||
continue
|
||||
command = [qemu_img_path, "snapshot", "-d", snapshot_name, disk]
|
||||
retcode = yield from self._qemu_img_exec(command)
|
||||
retcode = await self._qemu_img_exec(command)
|
||||
if retcode:
|
||||
stdout = self.read_qemu_img_stdout()
|
||||
log.warning("Could not delete saved VM state from disk {}: {}".format(disk, stdout))
|
||||
@@ -1860,8 +1837,7 @@ class QemuVM(BaseNode):
|
||||
except subprocess.SubprocessError as e:
|
||||
raise QemuError("Error while looking for the Qemu VM saved state snapshot: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _saved_state_option(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
async def _saved_state_option(self, snapshot_name="GNS3_SAVED_STATE"):
|
||||
|
||||
drives = ["a", "b", "c", "d"]
|
||||
qemu_img_path = self._get_qemu_img()
|
||||
@@ -1876,7 +1852,7 @@ class QemuVM(BaseNode):
|
||||
disk = disk_image
|
||||
if not os.path.exists(disk):
|
||||
continue
|
||||
output = yield from subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
|
||||
output = await subprocess_check_output(qemu_img_path, "info", "--output=json", disk)
|
||||
json_data = json.loads(output)
|
||||
if "snapshots" in json_data:
|
||||
for snapshot in json_data["snapshots"]:
|
||||
@@ -1889,8 +1865,7 @@ class QemuVM(BaseNode):
|
||||
raise QemuError("Error while looking for the Qemu VM saved state snapshot: {}".format(e))
|
||||
return []
|
||||
|
||||
@asyncio.coroutine
|
||||
def _build_command(self):
|
||||
async def _build_command(self):
|
||||
"""
|
||||
Command to start the QEMU process.
|
||||
(to be passed to subprocess.Popen())
|
||||
@@ -1905,10 +1880,10 @@ class QemuVM(BaseNode):
|
||||
command.extend(["-name", self._name])
|
||||
command.extend(["-m", "{}M".format(self._ram)])
|
||||
command.extend(["-smp", "cpus={}".format(self._cpus)])
|
||||
if (yield from self._run_with_hardware_acceleration(self.qemu_path, self._options)):
|
||||
if (await self._run_with_hardware_acceleration(self.qemu_path, self._options)):
|
||||
if sys.platform.startswith("linux"):
|
||||
command.extend(["-enable-kvm"])
|
||||
version = yield from self.manager.get_qemu_version(self.qemu_path)
|
||||
version = await self.manager.get_qemu_version(self.qemu_path)
|
||||
# Issue on some combo Intel CPU + KVM + Qemu 2.4.0
|
||||
# https://github.com/GNS3/gns3-server/issues/685
|
||||
if version and parse_version(version) >= parse_version("2.4.0") and self.platform == "x86_64":
|
||||
@@ -1918,7 +1893,7 @@ class QemuVM(BaseNode):
|
||||
command.extend(["-boot", "order={}".format(self._boot_priority)])
|
||||
command.extend(self._bios_option())
|
||||
command.extend(self._cdrom_option())
|
||||
command.extend((yield from self._disk_options()))
|
||||
command.extend((await self._disk_options()))
|
||||
command.extend(self._linux_boot_options())
|
||||
if "-uuid" not in additional_options:
|
||||
command.extend(["-uuid", self._id])
|
||||
@@ -1933,12 +1908,12 @@ class QemuVM(BaseNode):
|
||||
elif self._console_type != "none":
|
||||
raise QemuError("Console type {} is unknown".format(self._console_type))
|
||||
command.extend(self._monitor_options())
|
||||
command.extend((yield from self._network_options()))
|
||||
command.extend((await self._network_options()))
|
||||
command.extend(self._graphic())
|
||||
if self.on_close != "save_vm_state":
|
||||
yield from self._clear_save_vm_stated()
|
||||
await self._clear_save_vm_stated()
|
||||
else:
|
||||
command.extend((yield from self._saved_state_option()))
|
||||
command.extend((await self._saved_state_option()))
|
||||
|
||||
if additional_options:
|
||||
try:
|
||||
|
||||
@@ -34,12 +34,11 @@ class TraceNG(BaseManager):
|
||||
|
||||
super().__init__()
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new TraceNG VM.
|
||||
|
||||
:returns: TraceNGVM instance
|
||||
"""
|
||||
|
||||
return (yield from super().create_node(*args, **kwargs))
|
||||
return (await super().create_node(*args, **kwargs))
|
||||
@@ -68,13 +68,12 @@ class TraceNGVM(BaseNode):
|
||||
def ethernet_adapter(self):
|
||||
return self._ethernet_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this TraceNG VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
@@ -86,15 +85,14 @@ class TraceNGVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(self._local_udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnel = None
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_requirements(self):
|
||||
async def _check_requirements(self):
|
||||
"""
|
||||
Check if TraceNG is available.
|
||||
"""
|
||||
@@ -193,33 +191,32 @@ class TraceNGVM(BaseNode):
|
||||
id=self.id,
|
||||
destination=destination))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self, destination=None):
|
||||
async def start(self, destination=None):
|
||||
"""
|
||||
Starts the TraceNG process.
|
||||
"""
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
raise TraceNGError("Sorry, TraceNG can only run on Windows")
|
||||
yield from self._check_requirements()
|
||||
await self._check_requirements()
|
||||
if not self.is_running():
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
command = self._build_command(destination)
|
||||
yield from self._stop_ubridge() # make use we start with a fresh uBridge instance
|
||||
await self._stop_ubridge() # make use we start with a fresh uBridge instance
|
||||
try:
|
||||
log.info("Starting TraceNG: {}".format(command))
|
||||
flags = 0
|
||||
if hasattr(subprocess, "CREATE_NEW_CONSOLE"):
|
||||
flags = subprocess.CREATE_NEW_CONSOLE
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
cwd=self.working_dir,
|
||||
creationflags=flags)
|
||||
monitor_process(self._process, self._termination_callback)
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
log.info("TraceNG instance {} started PID={}".format(self.name, self._process.pid))
|
||||
self._started = True
|
||||
@@ -243,18 +240,17 @@ class TraceNGVM(BaseNode):
|
||||
if returncode != 0:
|
||||
self.project.emit("log.error", {"message": "TraceNG process has stopped, return code: {}\n".format(returncode)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the TraceNG process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
@@ -266,16 +262,15 @@ class TraceNGVM(BaseNode):
|
||||
|
||||
self._process = None
|
||||
self._started = False
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the TraceNG process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start(self._destination)
|
||||
await self.stop()
|
||||
await self.start(self._destination)
|
||||
|
||||
def _terminate_process(self):
|
||||
"""
|
||||
@@ -303,8 +298,7 @@ class TraceNGVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_add_nio_binding(self, port_number, nio):
|
||||
async def port_add_nio_binding(self, port_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@@ -317,7 +311,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
self._ethernet_adapter.add_nio(port_number, nio)
|
||||
log.info('TraceNG "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
|
||||
@@ -327,16 +321,14 @@ class TraceNGVM(BaseNode):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_update_nio_binding(self, port_number, nio):
|
||||
async def port_update_nio_binding(self, port_number, nio):
|
||||
if not self._ethernet_adapter.port_exists(port_number):
|
||||
raise TraceNGError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
port_number=port_number))
|
||||
if self.is_running():
|
||||
yield from self.update_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.update_ubridge_udp_connection("TraceNG-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_remove_nio_binding(self, port_number):
|
||||
async def port_remove_nio_binding(self, port_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@@ -350,7 +342,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="TraceNG-{}".format(self._id)))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="TraceNG-{}".format(self._id)))
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(port_number)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -363,8 +355,7 @@ class TraceNGVM(BaseNode):
|
||||
port_number=port_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file):
|
||||
async def start_capture(self, port_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -387,15 +378,14 @@ class TraceNGVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="TraceNG-{}".format(self._id),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="TraceNG-{}".format(self._id),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("TraceNG '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -414,7 +404,7 @@ class TraceNGVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="TraceNG-{}".format(self._id)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="TraceNG-{}".format(self._id)))
|
||||
|
||||
log.info("TraceNG '{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
||||
@@ -93,13 +93,12 @@ class VirtualBox(BaseManager):
|
||||
self._vboxmanage_path = vboxmanage_path
|
||||
return vboxmanage_path
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute(self, subcommand, args, timeout=60):
|
||||
async def execute(self, subcommand, args, timeout=60):
|
||||
|
||||
# We use a lock prevent parallel execution due to strange errors
|
||||
# reported by a user and reproduced by us.
|
||||
# https://github.com/GNS3/gns3-gui/issues/261
|
||||
with (yield from self._execute_lock):
|
||||
async with self._execute_lock:
|
||||
vboxmanage_path = self.vboxmanage_path
|
||||
if not vboxmanage_path:
|
||||
vboxmanage_path = self.find_vboxmanage()
|
||||
@@ -111,12 +110,12 @@ class VirtualBox(BaseManager):
|
||||
command_string = " ".join(command)
|
||||
log.info("Executing VBoxManage with command: {}".format(command_string))
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VirtualBoxError("Could not execute VBoxManage: {}".format(e))
|
||||
|
||||
try:
|
||||
stdout_data, stderr_data = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
stdout_data, stderr_data = await asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise VirtualBoxError("VBoxManage has timed out after {} seconds!".format(timeout))
|
||||
|
||||
@@ -126,15 +125,14 @@ class VirtualBox(BaseManager):
|
||||
|
||||
return stdout_data.decode("utf-8", errors="ignore").splitlines()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _find_inaccessible_hdd_files(self):
|
||||
async def _find_inaccessible_hdd_files(self):
|
||||
"""
|
||||
Finds inaccessible disk files (to clean up the VirtualBox media manager)
|
||||
"""
|
||||
|
||||
hdds = []
|
||||
try:
|
||||
properties = yield from self.execute("list", ["hdds"])
|
||||
properties = await self.execute("list", ["hdds"])
|
||||
# If VirtualBox is not available we have no inaccessible hdd
|
||||
except VirtualBoxError:
|
||||
return hdds
|
||||
@@ -152,32 +150,30 @@ class VirtualBox(BaseManager):
|
||||
flag_inaccessible = False
|
||||
return reversed(hdds)
|
||||
|
||||
@asyncio.coroutine
|
||||
def project_closed(self, project):
|
||||
async def project_closed(self, project):
|
||||
"""
|
||||
Called when a project is closed.
|
||||
|
||||
:param project: Project instance
|
||||
"""
|
||||
|
||||
yield from super().project_closed(project)
|
||||
hdd_files_to_close = yield from self._find_inaccessible_hdd_files()
|
||||
await super().project_closed(project)
|
||||
hdd_files_to_close = await self._find_inaccessible_hdd_files()
|
||||
for hdd_file in hdd_files_to_close:
|
||||
log.info("Closing VirtualBox VM disk file {}".format(os.path.basename(hdd_file)))
|
||||
try:
|
||||
yield from self.execute("closemedium", ["disk", hdd_file])
|
||||
await self.execute("closemedium", ["disk", hdd_file])
|
||||
except VirtualBoxError as e:
|
||||
log.warning("Could not close VirtualBox VM disk file {}: {}".format(os.path.basename(hdd_file), e))
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_vms(self, allow_clone=False):
|
||||
async def list_vms(self, allow_clone=False):
|
||||
"""
|
||||
Gets VirtualBox VM list.
|
||||
"""
|
||||
|
||||
vbox_vms = []
|
||||
result = yield from self.execute("list", ["vms"])
|
||||
result = await self.execute("list", ["vms"])
|
||||
for line in result:
|
||||
if len(line) == 0 or line[0] != '"' or line[-1:] != "}":
|
||||
continue # Broken output (perhaps a carriage return in VM name)
|
||||
@@ -185,10 +181,10 @@ class VirtualBox(BaseManager):
|
||||
vmname = vmname.strip('"')
|
||||
if vmname == "<inaccessible>":
|
||||
continue # ignore inaccessible VMs
|
||||
extra_data = yield from self.execute("getextradata", [vmname, "GNS3/Clone"])
|
||||
extra_data = await self.execute("getextradata", [vmname, "GNS3/Clone"])
|
||||
if allow_clone or len(extra_data) == 0 or not extra_data[0].strip() == "Value: yes":
|
||||
# get the amount of RAM
|
||||
info_results = yield from self.execute("showvminfo", [vmname, "--machinereadable"])
|
||||
info_results = await self.execute("showvminfo", [vmname, "--machinereadable"])
|
||||
ram = 0
|
||||
for info in info_results:
|
||||
try:
|
||||
|
||||
@@ -98,10 +98,9 @@ class VirtualBoxVM(BaseNode):
|
||||
def ethernet_adapters(self):
|
||||
return self._ethernet_adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_system_properties(self):
|
||||
async def _get_system_properties(self):
|
||||
|
||||
properties = yield from self.manager.execute("list", ["systemproperties"])
|
||||
properties = await self.manager.execute("list", ["systemproperties"])
|
||||
for prop in properties:
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@@ -109,15 +108,14 @@ class VirtualBoxVM(BaseNode):
|
||||
continue
|
||||
self._system_properties[name.strip()] = value.strip()
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_state(self):
|
||||
async def _get_vm_state(self):
|
||||
"""
|
||||
Returns the VM state (e.g. running, paused etc.)
|
||||
|
||||
:returns: state (string)
|
||||
"""
|
||||
|
||||
results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in results:
|
||||
if '=' in info:
|
||||
name, value = info.split('=', 1)
|
||||
@@ -125,8 +123,7 @@ class VirtualBoxVM(BaseNode):
|
||||
return value.strip('"')
|
||||
raise VirtualBoxError("Could not get VM state for {}".format(self._vmname))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, params):
|
||||
async def _control_vm(self, params):
|
||||
"""
|
||||
Change setting in this VM when running.
|
||||
|
||||
@@ -136,11 +133,10 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
result = yield from self.manager.execute("controlvm", [self._vmname] + args)
|
||||
result = await self.manager.execute("controlvm", [self._vmname] + args)
|
||||
return result
|
||||
|
||||
@asyncio.coroutine
|
||||
def _modify_vm(self, params):
|
||||
async def _modify_vm(self, params):
|
||||
"""
|
||||
Change setting in this VM when not running.
|
||||
|
||||
@@ -148,10 +144,9 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
yield from self.manager.execute("modifyvm", [self._vmname] + args)
|
||||
await self.manager.execute("modifyvm", [self._vmname] + args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_duplicate_linked_clone(self):
|
||||
async def _check_duplicate_linked_clone(self):
|
||||
"""
|
||||
Without linked clone two VM using the same image can't run
|
||||
at the same time.
|
||||
@@ -175,14 +170,13 @@ class VirtualBoxVM(BaseNode):
|
||||
if not found:
|
||||
return
|
||||
trial += 1
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
if not self.linked_clone:
|
||||
yield from self._check_duplicate_linked_clone()
|
||||
await self._check_duplicate_linked_clone()
|
||||
|
||||
yield from self._get_system_properties()
|
||||
await self._get_system_properties()
|
||||
if "API version" not in self._system_properties:
|
||||
raise VirtualBoxError("Can't access to VirtualBox API version:\n{}".format(self._system_properties))
|
||||
if parse_version(self._system_properties["API version"]) < parse_version("4_3"):
|
||||
@@ -192,15 +186,15 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.linked_clone:
|
||||
if self.id and os.path.isdir(os.path.join(self.working_dir, self._vmname)):
|
||||
self._patch_vm_uuid()
|
||||
yield from self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||
yield from self._reattach_linked_hdds()
|
||||
await self.manager.execute("registervm", [self._linked_vbox_file()])
|
||||
await self._reattach_linked_hdds()
|
||||
else:
|
||||
yield from self._create_linked_clone()
|
||||
await self._create_linked_clone()
|
||||
|
||||
if self._adapters:
|
||||
yield from self.set_adapters(self._adapters)
|
||||
await self.set_adapters(self._adapters)
|
||||
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
if "memory" in vm_info:
|
||||
self._ram = int(vm_info["memory"])
|
||||
|
||||
@@ -234,22 +228,20 @@ class VirtualBoxVM(BaseNode):
|
||||
machine.set("uuid", "{" + self.id + "}")
|
||||
tree.write(self._linked_vbox_file())
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_hw_virtualization(self):
|
||||
async def check_hw_virtualization(self):
|
||||
"""
|
||||
Returns either hardware virtualization is activated or not.
|
||||
|
||||
:returns: boolean
|
||||
"""
|
||||
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
if "hwvirtex" in vm_info and vm_info["hwvirtex"] == "on":
|
||||
return True
|
||||
return False
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this VirtualBox VM.
|
||||
"""
|
||||
@@ -258,17 +250,17 @@ class VirtualBoxVM(BaseNode):
|
||||
return
|
||||
|
||||
# resume the VM if it is paused
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "paused":
|
||||
yield from self.resume()
|
||||
await self.resume()
|
||||
return
|
||||
|
||||
# VM must be powered off to start it
|
||||
if vm_state != "poweroff":
|
||||
raise VirtualBoxError("VirtualBox VM not powered off")
|
||||
|
||||
yield from self._set_network_options()
|
||||
yield from self._set_serial_console()
|
||||
await self._set_network_options()
|
||||
await self._set_serial_console()
|
||||
|
||||
# check if there is enough RAM to run
|
||||
self.check_available_ram(self.ram)
|
||||
@@ -276,92 +268,90 @@ class VirtualBoxVM(BaseNode):
|
||||
args = [self._vmname]
|
||||
if self._headless:
|
||||
args.extend(["--type", "headless"])
|
||||
result = yield from self.manager.execute("startvm", args)
|
||||
result = await self.manager.execute("startvm", args)
|
||||
self.status = "started"
|
||||
log.info("VirtualBox VM '{name}' [{id}] started".format(name=self.name, id=self.id))
|
||||
log.debug("Start result: {}".format(result))
|
||||
|
||||
# add a guest property to let the VM know about the GNS3 name
|
||||
yield from self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
||||
await self.manager.execute("guestproperty", ["set", self._vmname, "NameInGNS3", self.name])
|
||||
# add a guest property to let the VM know about the GNS3 project directory
|
||||
yield from self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
||||
await self.manager.execute("guestproperty", ["set", self._vmname, "ProjectDirInGNS3", self.working_dir])
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
|
||||
if (yield from self.check_hw_virtualization()):
|
||||
if (await self.check_hw_virtualization()):
|
||||
self._hw_virtualization = True
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this VirtualBox VM.
|
||||
"""
|
||||
|
||||
self._hw_virtualization = False
|
||||
yield from self._stop_ubridge()
|
||||
yield from self._stop_remote_console()
|
||||
vm_state = yield from self._get_vm_state()
|
||||
await self._stop_ubridge()
|
||||
await self._stop_remote_console()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
|
||||
|
||||
if self.on_close == "save_vm_state":
|
||||
result = yield from self._control_vm("savestate")
|
||||
result = await self._control_vm("savestate")
|
||||
self.status = "stopped"
|
||||
log.debug("Stop result: {}".format(result))
|
||||
elif self.on_close == "shutdown_signal":
|
||||
# use ACPI to shutdown the VM
|
||||
result = yield from self._control_vm("acpipowerbutton")
|
||||
result = await self._control_vm("acpipowerbutton")
|
||||
trial = 0
|
||||
while True:
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "poweroff":
|
||||
break
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
trial += 1
|
||||
if trial >= 120:
|
||||
yield from self._control_vm("poweroff")
|
||||
await self._control_vm("poweroff")
|
||||
break
|
||||
self.status = "stopped"
|
||||
log.debug("ACPI shutdown result: {}".format(result))
|
||||
else:
|
||||
# power off the VM
|
||||
result = yield from self._control_vm("poweroff")
|
||||
result = await self._control_vm("poweroff")
|
||||
self.status = "stopped"
|
||||
log.debug("Stop result: {}".format(result))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
|
||||
yield from asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
|
||||
await asyncio.sleep(0.5) # give some time for VirtualBox to unlock the VM
|
||||
try:
|
||||
# deactivate the first serial port
|
||||
yield from self._modify_vm("--uart1 off")
|
||||
await self._modify_vm("--uart1 off")
|
||||
except VirtualBoxError as e:
|
||||
log.warning("Could not deactivate the first serial port: {}".format(e))
|
||||
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
yield from super().stop()
|
||||
await self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this VirtualBox VM.
|
||||
"""
|
||||
|
||||
vm_state = yield from self._get_vm_state()
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running":
|
||||
yield from self._control_vm("pause")
|
||||
await self._control_vm("pause")
|
||||
self.status = "suspended"
|
||||
log.info("VirtualBox VM '{name}' [{id}] suspended".format(name=self.name, id=self.id))
|
||||
else:
|
||||
@@ -369,31 +359,28 @@ class VirtualBoxVM(BaseNode):
|
||||
id=self.id,
|
||||
state=vm_state))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this VirtualBox VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("resume")
|
||||
await self._control_vm("resume")
|
||||
self.status = "started"
|
||||
log.info("VirtualBox VM '{name}' [{id}] resumed".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this VirtualBox VM.
|
||||
"""
|
||||
|
||||
result = yield from self._control_vm("reset")
|
||||
result = await self._control_vm("reset")
|
||||
log.info("VirtualBox VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id))
|
||||
log.debug("Reload result: {}".format(result))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_all_hdd_files(self):
|
||||
async def _get_all_hdd_files(self):
|
||||
|
||||
hdds = []
|
||||
properties = yield from self.manager.execute("list", ["hdds"])
|
||||
properties = await self.manager.execute("list", ["hdds"])
|
||||
for prop in properties:
|
||||
try:
|
||||
name, value = prop.split(':', 1)
|
||||
@@ -403,8 +390,7 @@ class VirtualBoxVM(BaseNode):
|
||||
hdds.append(value.strip())
|
||||
return hdds
|
||||
|
||||
@asyncio.coroutine
|
||||
def _reattach_linked_hdds(self):
|
||||
async def _reattach_linked_hdds(self):
|
||||
"""
|
||||
Reattach linked cloned hard disks.
|
||||
"""
|
||||
@@ -428,7 +414,7 @@ class VirtualBoxVM(BaseNode):
|
||||
medium=hdd_file))
|
||||
|
||||
try:
|
||||
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
|
||||
await self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium "{}"'.format(hdd_info["controller"],
|
||||
hdd_info["port"],
|
||||
hdd_info["device"],
|
||||
hdd_file))
|
||||
@@ -443,8 +429,7 @@ class VirtualBoxVM(BaseNode):
|
||||
error=e))
|
||||
continue
|
||||
|
||||
@asyncio.coroutine
|
||||
def save_linked_hdds_info(self):
|
||||
async def save_linked_hdds_info(self):
|
||||
"""
|
||||
Save linked cloned hard disks information.
|
||||
|
||||
@@ -454,8 +439,8 @@ class VirtualBoxVM(BaseNode):
|
||||
hdd_table = []
|
||||
if self.linked_clone:
|
||||
if os.path.exists(self.working_dir):
|
||||
hdd_files = yield from self._get_all_hdd_files()
|
||||
vm_info = yield from self._get_vm_info()
|
||||
hdd_files = await self._get_all_hdd_files()
|
||||
vm_info = await self._get_vm_info()
|
||||
for entry, value in vm_info.items():
|
||||
match = re.search("^([\s\w]+)\-(\d)\-(\d)$", entry) # match Controller-PortNumber-DeviceNumber entry
|
||||
if match:
|
||||
@@ -489,8 +474,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return hdd_table
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VirtualBox VM.
|
||||
"""
|
||||
@@ -499,7 +483,7 @@ class VirtualBoxVM(BaseNode):
|
||||
# VM is already closed
|
||||
return
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
log.debug("VirtualBox VM '{name}' [{id}] is closing".format(name=self.name, id=self.id))
|
||||
@@ -519,10 +503,10 @@ class VirtualBoxVM(BaseNode):
|
||||
self._local_udp_tunnels = {}
|
||||
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
|
||||
if self.linked_clone:
|
||||
hdd_table = yield from self.save_linked_hdds_info()
|
||||
hdd_table = await self.save_linked_hdds_info()
|
||||
for hdd in hdd_table.copy():
|
||||
log.info("VirtualBox VM '{name}' [{id}] detaching HDD {controller} {port} {device}".format(name=self.name,
|
||||
id=self.id,
|
||||
@@ -530,7 +514,7 @@ class VirtualBoxVM(BaseNode):
|
||||
port=hdd["port"],
|
||||
device=hdd["device"]))
|
||||
try:
|
||||
yield from self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
|
||||
await self._storage_attach('--storagectl "{}" --port {} --device {} --type hdd --medium none'.format(hdd["controller"],
|
||||
hdd["port"],
|
||||
hdd["device"]))
|
||||
except VirtualBoxError as e:
|
||||
@@ -543,7 +527,7 @@ class VirtualBoxVM(BaseNode):
|
||||
continue
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] unregistering".format(name=self.name, id=self.id))
|
||||
yield from self.manager.execute("unregistervm", [self._name])
|
||||
await self.manager.execute("unregistervm", [self._name])
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] closed".format(name=self.name, id=self.id))
|
||||
self._closed = True
|
||||
@@ -603,8 +587,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._ram
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_ram(self, ram):
|
||||
async def set_ram(self, ram):
|
||||
"""
|
||||
Set the amount of RAM allocated to this VirtualBox VM.
|
||||
|
||||
@@ -614,7 +597,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if ram == 0:
|
||||
return
|
||||
|
||||
yield from self._modify_vm('--memory {}'.format(ram))
|
||||
await self._modify_vm('--memory {}'.format(ram))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] has set amount of RAM to {ram}".format(name=self.name, id=self.id, ram=ram))
|
||||
self._ram = ram
|
||||
@@ -629,8 +612,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._vmname
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_vmname(self, vmname):
|
||||
async def set_vmname(self, vmname):
|
||||
"""
|
||||
Renames the VirtualBox VM.
|
||||
|
||||
@@ -644,10 +626,10 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.status == "started":
|
||||
raise VirtualBoxError("You can't change the name of running VM {}".format(self._name))
|
||||
# We can't rename a VM to name that already exists
|
||||
vms = yield from self.manager.list_vms(allow_clone=True)
|
||||
vms = await self.manager.list_vms(allow_clone=True)
|
||||
if vmname in [vm["vmname"] for vm in vms]:
|
||||
raise VirtualBoxError("You can't change the name to {} it's already use in VirtualBox".format(vmname))
|
||||
yield from self._modify_vm('--name "{}"'.format(vmname))
|
||||
await self._modify_vm('--name "{}"'.format(vmname))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}] has set the VM name to '{vmname}'".format(name=self.name, id=self.id, vmname=vmname))
|
||||
self._vmname = vmname
|
||||
@@ -662,8 +644,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
return self._adapters
|
||||
|
||||
@asyncio.coroutine
|
||||
def set_adapters(self, adapters):
|
||||
async def set_adapters(self, adapters):
|
||||
"""
|
||||
Sets the number of Ethernet adapters for this VirtualBox VM instance.
|
||||
|
||||
@@ -671,7 +652,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
# check for the maximum adapters supported by the VM
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
chipset = "piix3" # default chipset for VirtualBox VMs
|
||||
self._maximum_adapters = 8 # default maximum network adapter count for PIIX3 chipset
|
||||
if "chipset" in vm_info:
|
||||
@@ -749,8 +730,7 @@ class VirtualBoxVM(BaseNode):
|
||||
id=self.id,
|
||||
adapter_type=adapter_type))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_vm_info(self):
|
||||
async def _get_vm_info(self):
|
||||
"""
|
||||
Returns this VM info.
|
||||
|
||||
@@ -758,7 +738,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
vm_info = {}
|
||||
results = yield from self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
results = await self.manager.execute("showvminfo", [self._vmname, "--machinereadable"])
|
||||
for info in results:
|
||||
try:
|
||||
name, value = info.split('=', 1)
|
||||
@@ -784,22 +764,20 @@ class VirtualBoxVM(BaseNode):
|
||||
raise VirtualBoxError("Could not create the VirtualBox pipe directory: {}".format(e))
|
||||
return pipe_name
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_serial_console(self):
|
||||
async def _set_serial_console(self):
|
||||
"""
|
||||
Configures the first serial port to allow a serial console connection.
|
||||
"""
|
||||
|
||||
# activate the first serial port
|
||||
yield from self._modify_vm("--uart1 0x3F8 4")
|
||||
await self._modify_vm("--uart1 0x3F8 4")
|
||||
|
||||
# set server mode with a pipe on the first serial port
|
||||
pipe_name = self._get_pipe_name()
|
||||
args = [self._vmname, "--uartmode1", "server", pipe_name]
|
||||
yield from self.manager.execute("modifyvm", args)
|
||||
await self.manager.execute("modifyvm", args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _storage_attach(self, params):
|
||||
async def _storage_attach(self, params):
|
||||
"""
|
||||
Change storage medium in this VM.
|
||||
|
||||
@@ -807,10 +785,9 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
args = shlex.split(params)
|
||||
yield from self.manager.execute("storageattach", [self._vmname] + args)
|
||||
await self.manager.execute("storageattach", [self._vmname] + args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _get_nic_attachements(self, maximum_adapters):
|
||||
async def _get_nic_attachements(self, maximum_adapters):
|
||||
"""
|
||||
Returns NIC attachements.
|
||||
|
||||
@@ -819,7 +796,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
|
||||
nics = []
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
for adapter_number in range(0, maximum_adapters):
|
||||
entry = "nic{}".format(adapter_number + 1)
|
||||
if entry in vm_info:
|
||||
@@ -829,22 +806,21 @@ class VirtualBoxVM(BaseNode):
|
||||
nics.append(None)
|
||||
return nics
|
||||
|
||||
@asyncio.coroutine
|
||||
def _set_network_options(self):
|
||||
async def _set_network_options(self):
|
||||
"""
|
||||
Configures network options.
|
||||
"""
|
||||
|
||||
nic_attachments = yield from self._get_nic_attachements(self._maximum_adapters)
|
||||
nic_attachments = await self._get_nic_attachements(self._maximum_adapters)
|
||||
for adapter_number in range(0, self._adapters):
|
||||
attachment = nic_attachments[adapter_number]
|
||||
if attachment == "null":
|
||||
# disconnect the cable if no backend is attached.
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
if attachment == "none":
|
||||
# set the backend to null to avoid a difference in the number of interfaces in the Guest.
|
||||
yield from self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} null".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
|
||||
# use a local UDP tunnel to connect to uBridge instead
|
||||
if adapter_number not in self._local_udp_tunnels:
|
||||
@@ -855,7 +831,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if not self._use_any_adapter and attachment in ("nat", "bridged", "intnet", "hostonly", "natnetwork"):
|
||||
continue
|
||||
|
||||
yield from self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--nictrace{} off".format(adapter_number + 1))
|
||||
|
||||
custom_adapter = self._get_custom_adapter_settings(adapter_number)
|
||||
adapter_type = custom_adapter.get("adapter_type", self._adapter_type)
|
||||
@@ -874,45 +850,44 @@ class VirtualBoxVM(BaseNode):
|
||||
if adapter_type == "Paravirtualized Network (virtio-net)":
|
||||
vbox_adapter_type = "virtio"
|
||||
args = [self._vmname, "--nictype{}".format(adapter_number + 1), vbox_adapter_type]
|
||||
yield from self.manager.execute("modifyvm", args)
|
||||
await self.manager.execute("modifyvm", args)
|
||||
|
||||
if isinstance(nio, NIOUDP):
|
||||
log.debug("setting UDP params on adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
yield from self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
yield from self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
await self._modify_vm("--nic{} generic".format(adapter_number + 1))
|
||||
await self._modify_vm("--nicgenericdrv{} UDPTunnel".format(adapter_number + 1))
|
||||
await self._modify_vm("--nicproperty{} sport={}".format(adapter_number + 1, nio.lport))
|
||||
await self._modify_vm("--nicproperty{} dest={}".format(adapter_number + 1, nio.rhost))
|
||||
await self._modify_vm("--nicproperty{} dport={}".format(adapter_number + 1, nio.rport))
|
||||
if nio.suspend:
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
else:
|
||||
yield from self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} on".format(adapter_number + 1))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._modify_vm("--nictrace{} on".format(adapter_number + 1))
|
||||
yield from self._modify_vm('--nictracefile{} "{}"'.format(adapter_number + 1, nio.pcap_output_file))
|
||||
await self._modify_vm("--nictrace{} on".format(adapter_number + 1))
|
||||
await self._modify_vm('--nictracefile{} "{}"'.format(adapter_number + 1, nio.pcap_output_file))
|
||||
|
||||
if not self._ethernet_adapters[adapter_number].get_nio(0):
|
||||
yield from self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
await self._modify_vm("--cableconnected{} off".format(adapter_number + 1))
|
||||
|
||||
for adapter_number in range(self._adapters, self._maximum_adapters):
|
||||
log.debug("disabling remaining adapter {}".format(adapter_number))
|
||||
yield from self._modify_vm("--nic{} none".format(adapter_number + 1))
|
||||
await self._modify_vm("--nic{} none".format(adapter_number + 1))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _create_linked_clone(self):
|
||||
async def _create_linked_clone(self):
|
||||
"""
|
||||
Creates a new linked clone.
|
||||
"""
|
||||
|
||||
gns3_snapshot_exists = False
|
||||
vm_info = yield from self._get_vm_info()
|
||||
vm_info = await self._get_vm_info()
|
||||
for entry, value in vm_info.items():
|
||||
if entry.startswith("SnapshotName") and value == "GNS3 Linked Base for clones":
|
||||
gns3_snapshot_exists = True
|
||||
|
||||
if not gns3_snapshot_exists:
|
||||
result = yield from self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
||||
result = await self.manager.execute("snapshot", [self._vmname, "take", "GNS3 Linked Base for clones"])
|
||||
log.debug("GNS3 snapshot created: {}".format(result))
|
||||
|
||||
args = [self._vmname,
|
||||
@@ -926,17 +901,17 @@ class VirtualBoxVM(BaseNode):
|
||||
self.working_dir,
|
||||
"--register"]
|
||||
|
||||
result = yield from self.manager.execute("clonevm", args)
|
||||
result = await self.manager.execute("clonevm", args)
|
||||
log.debug("VirtualBox VM: {} cloned".format(result))
|
||||
|
||||
self._vmname = self._name
|
||||
yield from self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
||||
await self.manager.execute("setextradata", [self._vmname, "GNS3/Clone", "yes"])
|
||||
|
||||
# We create a reset snapshot in order to simplify life of user who want to rollback their VM
|
||||
# Warning: Do not document this it's seem buggy we keep it because Raizo students use it.
|
||||
try:
|
||||
args = [self._vmname, "take", "reset"]
|
||||
result = yield from self.manager.execute("snapshot", args)
|
||||
result = await self.manager.execute("snapshot", args)
|
||||
log.debug("Snapshot 'reset' created: {}".format(result))
|
||||
# It seem sometimes this failed due to internal race condition of Vbox
|
||||
# we have no real explanation of this.
|
||||
@@ -945,8 +920,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
os.makedirs(os.path.join(self.working_dir, self._vmname), exist_ok=True)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts remote console support for this VM.
|
||||
"""
|
||||
@@ -954,7 +928,7 @@ class VirtualBoxVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
pipe_name = self._get_pipe_name()
|
||||
try:
|
||||
self._remote_pipe = yield from asyncio_open_serial(pipe_name)
|
||||
self._remote_pipe = await asyncio_open_serial(pipe_name)
|
||||
except OSError as e:
|
||||
raise VirtualBoxError("Could not open serial pipe '{}': {}".format(pipe_name, e))
|
||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
||||
@@ -962,18 +936,17 @@ class VirtualBoxVM(BaseNode):
|
||||
binary=True,
|
||||
echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_remote_console(self):
|
||||
async def _stop_remote_console(self):
|
||||
"""
|
||||
Stops remote console support for this VM.
|
||||
"""
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
yield from self._telnet_server.wait_closed()
|
||||
await self._telnet_server.wait_closed()
|
||||
self._remote_pipe.close()
|
||||
self._telnet_server = None
|
||||
|
||||
@@ -990,8 +963,7 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
super(VirtualBoxVM, VirtualBoxVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@@ -1006,7 +978,7 @@ class VirtualBoxVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
# check if trying to connect to a nat, bridged, host-only or any other special adapter
|
||||
nic_attachments = yield from self._get_nic_attachements(self._maximum_adapters)
|
||||
nic_attachments = await self._get_nic_attachements(self._maximum_adapters)
|
||||
attachment = nic_attachments[adapter_number]
|
||||
if attachment in ("nat", "bridged", "intnet", "hostonly", "natnetwork"):
|
||||
if not self._use_any_adapter:
|
||||
@@ -1018,21 +990,21 @@ class VirtualBoxVM(BaseNode):
|
||||
# dynamically configure an UDP tunnel attachment if the VM is already running
|
||||
local_nio = self._local_udp_tunnels[adapter_number][0]
|
||||
if local_nio and isinstance(local_nio, NIOUDP):
|
||||
yield from self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
yield from self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, local_nio.lport))
|
||||
yield from self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, local_nio.rhost))
|
||||
yield from self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, local_nio.rport))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("nic{} generic UDPTunnel".format(adapter_number + 1))
|
||||
await self._control_vm("nicproperty{} sport={}".format(adapter_number + 1, local_nio.lport))
|
||||
await self._control_vm("nicproperty{} dest={}".format(adapter_number + 1, local_nio.rhost))
|
||||
await self._control_vm("nicproperty{} dport={}".format(adapter_number + 1, local_nio.rport))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.add_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
except KeyError:
|
||||
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
|
||||
adapter_number=adapter_number))
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
log.info("VirtualBox VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
@@ -1040,8 +1012,7 @@ class VirtualBoxVM(BaseNode):
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@@ -1051,21 +1022,20 @@ class VirtualBoxVM(BaseNode):
|
||||
|
||||
if self.is_running():
|
||||
try:
|
||||
yield from self.update_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self.update_ubridge_udp_connection("VBOX-{}-{}".format(self._id, adapter_number),
|
||||
self._local_udp_tunnels[adapter_number][1],
|
||||
nio)
|
||||
if nio.suspend:
|
||||
yield from self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
else:
|
||||
yield from self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} on".format(adapter_number + 1))
|
||||
except IndexError:
|
||||
raise VirtualBoxError('Adapter {adapter_number} does not exist on VirtualBox VM "{name}"'.format(
|
||||
name=self._name,
|
||||
adapter_number=adapter_number
|
||||
))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@@ -1081,10 +1051,10 @@ class VirtualBoxVM(BaseNode):
|
||||
adapter_number=adapter_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
vm_state = yield from self._get_vm_state()
|
||||
await self._ubridge_send("bridge delete {name}".format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
vm_state = await self._get_vm_state()
|
||||
if vm_state == "running":
|
||||
yield from self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
await self._control_vm("setlinkstate{} off".format(adapter_number + 1))
|
||||
|
||||
nio = adapter.get_nio(0)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -1103,8 +1073,7 @@ class VirtualBoxVM(BaseNode):
|
||||
"""
|
||||
return self.ubridge is not None
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -1129,14 +1098,14 @@ class VirtualBoxVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VBOX-{}-{}".format(self._id, adapter_number),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VBOX-{}-{}".format(self._id, adapter_number),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -1157,7 +1126,7 @@ class VirtualBoxVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="VBOX-{}-{}".format(self._id, adapter_number)))
|
||||
|
||||
log.info("VirtualBox VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
||||
@@ -32,7 +32,6 @@ import shlex
|
||||
from collections import OrderedDict
|
||||
from gns3server.utils.interfaces import interfaces
|
||||
from gns3server.utils.asyncio import subprocess_check_output
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
from gns3server.utils import parse_version
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -40,7 +39,6 @@ log = logging.getLogger(__name__)
|
||||
from gns3server.compute.base_manager import BaseManager
|
||||
from gns3server.compute.vmware.vmware_vm import VMwareVM
|
||||
from gns3server.compute.vmware.vmware_error import VMwareError
|
||||
from gns3server.utils.asyncio import asyncio_ensure_future
|
||||
|
||||
|
||||
class VMware(BaseManager):
|
||||
@@ -142,8 +140,7 @@ class VMware(BaseManager):
|
||||
version = match.group(1)
|
||||
return version
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vmware_player_requirements(self, player_version):
|
||||
async def _check_vmware_player_requirements(self, player_version):
|
||||
"""
|
||||
Check minimum requirements to use VMware Player.
|
||||
|
||||
@@ -159,17 +156,16 @@ class VMware(BaseManager):
|
||||
if player_version < 6:
|
||||
raise VMwareError("Using VMware Player requires version 6 or above")
|
||||
elif player_version == 6:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
elif player_version == 7:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
elif player_version >= 12:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
elif player_version >= 14:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
self._host_type = "player"
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vmware_workstation_requirements(self, ws_version):
|
||||
async def _check_vmware_workstation_requirements(self, ws_version):
|
||||
"""
|
||||
Check minimum requirements to use VMware Workstation.
|
||||
|
||||
@@ -185,17 +181,16 @@ class VMware(BaseManager):
|
||||
if ws_version < 10:
|
||||
raise VMwareError("Using VMware Workstation requires version 10 or above")
|
||||
elif ws_version == 10:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.13.0")
|
||||
elif ws_version == 11:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.14.0")
|
||||
elif ws_version >= 12:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.15.0")
|
||||
elif ws_version >= 14:
|
||||
yield from self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
await self.check_vmrun_version(minimum_required_version="1.17.0")
|
||||
self._host_type = "ws"
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_vmware_version(self):
|
||||
async def check_vmware_version(self):
|
||||
"""
|
||||
Check VMware version
|
||||
"""
|
||||
@@ -207,13 +202,13 @@ class VMware(BaseManager):
|
||||
player_version = self._find_vmware_version_registry(r"SOFTWARE\Wow6432Node\VMware, Inc.\VMware Player")
|
||||
if player_version:
|
||||
log.debug("VMware Player version {} detected".format(player_version))
|
||||
yield from self._check_vmware_player_requirements(player_version)
|
||||
await self._check_vmware_player_requirements(player_version)
|
||||
else:
|
||||
log.warning("Could not find VMware version")
|
||||
self._host_type = "ws"
|
||||
else:
|
||||
log.debug("VMware Workstation version {} detected".format(ws_version))
|
||||
yield from self._check_vmware_workstation_requirements(ws_version)
|
||||
await self._check_vmware_workstation_requirements(ws_version)
|
||||
else:
|
||||
if sys.platform.startswith("darwin"):
|
||||
if not os.path.isdir("/Applications/VMware Fusion.app"):
|
||||
@@ -226,20 +221,20 @@ class VMware(BaseManager):
|
||||
raise VMwareError("VMware is not installed (vmware or vmplayer executable could not be found in $PATH)")
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(vmware_path, "-v")
|
||||
output = await subprocess_check_output(vmware_path, "-v")
|
||||
match = re.search("VMware Workstation ([0-9]+)\.", output)
|
||||
version = None
|
||||
if match:
|
||||
# VMware Workstation has been detected
|
||||
version = match.group(1)
|
||||
log.debug("VMware Workstation version {} detected".format(version))
|
||||
yield from self._check_vmware_workstation_requirements(version)
|
||||
await self._check_vmware_workstation_requirements(version)
|
||||
match = re.search("VMware Player ([0-9]+)\.", output)
|
||||
if match:
|
||||
# VMware Player has been detected
|
||||
version = match.group(1)
|
||||
log.debug("VMware Player version {} detected".format(version))
|
||||
yield from self._check_vmware_player_requirements(version)
|
||||
await self._check_vmware_player_requirements(version)
|
||||
if version is None:
|
||||
log.warning("Could not find VMware version. Output of VMware: {}".format(output))
|
||||
raise VMwareError("Could not find VMware version. Output of VMware: {}".format(output))
|
||||
@@ -365,28 +360,26 @@ class VMware(BaseManager):
|
||||
|
||||
return self._host_type
|
||||
|
||||
@asyncio.coroutine
|
||||
def execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
async def execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
trial = 2
|
||||
|
||||
while True:
|
||||
try:
|
||||
return (yield from self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
return (await self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
except VMwareError as e:
|
||||
# We can fail to detect that it's VMware player instead of Workstation (due to marketing change Player is now Player Workstation)
|
||||
if self.host_type == "ws" and "VIX_SERVICEPROVIDER_VMWARE_WORKSTATION" in str(e):
|
||||
self._host_type = "player"
|
||||
return (yield from self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
return (await self._execute(subcommand, args, timeout=timeout, log_level=log_level))
|
||||
else:
|
||||
if trial <= 0:
|
||||
raise e
|
||||
trial -= 1
|
||||
yield from asyncio.sleep(0.5)
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
async def _execute(self, subcommand, args, timeout=120, log_level=logging.INFO):
|
||||
if self.host_type is None:
|
||||
yield from self.check_vmware_version()
|
||||
await self.check_vmware_version()
|
||||
|
||||
vmrun_path = self.vmrun_path
|
||||
if not vmrun_path:
|
||||
@@ -397,12 +390,12 @@ class VMware(BaseManager):
|
||||
command_string = " ".join([shlex.quote(c) for c in command])
|
||||
log.log(log_level, "Executing vmrun with command: {}".format(command_string))
|
||||
try:
|
||||
process = yield from asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
process = await asyncio.create_subprocess_exec(*command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VMwareError("Could not execute vmrun: {}".format(e))
|
||||
|
||||
try:
|
||||
stdout_data, _ = yield from asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
stdout_data, _ = await asyncio.wait_for(process.communicate(), timeout=timeout)
|
||||
except asyncio.TimeoutError:
|
||||
raise VMwareError("vmrun has timed out after {} seconds!\nTry to run {} in a terminal to see more details.\n\nMake sure GNS3 and VMware run under the same user and whitelist vmrun.exe in your antivirus.".format(timeout, command_string))
|
||||
|
||||
@@ -413,8 +406,7 @@ class VMware(BaseManager):
|
||||
|
||||
return stdout_data.decode("utf-8", errors="ignore").splitlines()
|
||||
|
||||
@asyncio.coroutine
|
||||
def check_vmrun_version(self, minimum_required_version="1.13.0"):
|
||||
async def check_vmrun_version(self, minimum_required_version="1.13.0"):
|
||||
"""
|
||||
Checks the vmrun version.
|
||||
|
||||
@@ -431,7 +423,7 @@ class VMware(BaseManager):
|
||||
vmrun_path = self.find_vmrun()
|
||||
|
||||
try:
|
||||
output = yield from subprocess_check_output(vmrun_path)
|
||||
output = await subprocess_check_output(vmrun_path)
|
||||
match = re.search("vmrun version ([0-9\.]+)", output)
|
||||
version = None
|
||||
if match:
|
||||
@@ -446,15 +438,14 @@ class VMware(BaseManager):
|
||||
log.error("Error while looking for the VMware vmrun version: {}".format(e))
|
||||
raise VMwareError("Error while looking for the VMware vmrun version: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def remove_from_vmware_inventory(self, vmx_path):
|
||||
async def remove_from_vmware_inventory(self, vmx_path):
|
||||
"""
|
||||
Removes a linked clone from the VMware inventory file.
|
||||
|
||||
:param vmx_path: path of the linked clone VMX file
|
||||
"""
|
||||
|
||||
with (yield from self._vmware_inventory_lock):
|
||||
async with self._vmware_inventory_lock:
|
||||
inventory_path = self.get_vmware_inventory_path()
|
||||
if os.path.exists(inventory_path):
|
||||
try:
|
||||
@@ -677,14 +668,13 @@ class VMware(BaseManager):
|
||||
else:
|
||||
return [os.path.expanduser("~/vmware")]
|
||||
|
||||
@asyncio.coroutine
|
||||
def list_vms(self):
|
||||
async def list_vms(self):
|
||||
"""
|
||||
Gets VMware VM list.
|
||||
"""
|
||||
|
||||
# check for the right VMware version
|
||||
yield from self.check_vmware_version()
|
||||
await self.check_vmware_version()
|
||||
vmware_vms = []
|
||||
inventory_path = self.get_vmware_inventory_path()
|
||||
if os.path.exists(inventory_path) and self.host_type != "player":
|
||||
@@ -746,4 +736,4 @@ if __name__ == '__main__':
|
||||
loop = asyncio.get_event_loop()
|
||||
vmware = VMware.instance()
|
||||
print("=> Check version")
|
||||
loop.run_until_complete(asyncio_ensure_future(vmware.check_vmware_version()))
|
||||
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))
|
||||
@@ -95,12 +95,11 @@ class VMwareVM(BaseNode):
|
||||
return self._vmnets
|
||||
|
||||
@locking
|
||||
@asyncio.coroutine
|
||||
def _control_vm(self, subcommand, *additional_args):
|
||||
async def _control_vm(self, subcommand, *additional_args):
|
||||
|
||||
args = [self._vmx_path]
|
||||
args.extend(additional_args)
|
||||
result = yield from self.manager.execute(subcommand, args)
|
||||
result = await self.manager.execute(subcommand, args)
|
||||
log.debug("Control VM '{}' result: {}".format(subcommand, result))
|
||||
return result
|
||||
|
||||
@@ -124,16 +123,14 @@ class VMwareVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise VMwareError('Could not write VMware VMX file "{}": {}'.format(self._vmx_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def is_running(self):
|
||||
async def is_running(self):
|
||||
|
||||
result = yield from self.manager.execute("list", [])
|
||||
result = await self.manager.execute("list", [])
|
||||
if self._vmx_path in result:
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_duplicate_linked_clone(self):
|
||||
async def _check_duplicate_linked_clone(self):
|
||||
"""
|
||||
Without linked clone two VM using the same image can't run
|
||||
at the same time.
|
||||
@@ -157,17 +154,16 @@ class VMwareVM(BaseNode):
|
||||
if not found:
|
||||
return
|
||||
trial += 1
|
||||
yield from asyncio.sleep(1)
|
||||
await asyncio.sleep(1)
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
async def create(self):
|
||||
"""
|
||||
Creates this VM and handle linked clones.
|
||||
"""
|
||||
if not self.linked_clone:
|
||||
yield from self._check_duplicate_linked_clone()
|
||||
await self._check_duplicate_linked_clone()
|
||||
|
||||
yield from self.manager.check_vmrun_version()
|
||||
await self.manager.check_vmrun_version()
|
||||
if self.linked_clone and not os.path.exists(os.path.join(self.working_dir, os.path.basename(self._vmx_path))):
|
||||
if self.manager.host_type == "player":
|
||||
raise VMwareError("Linked clones are not supported by VMware Player")
|
||||
@@ -187,11 +183,11 @@ class VMwareVM(BaseNode):
|
||||
break
|
||||
if not gns3_snapshot_exists:
|
||||
log.info("Creating snapshot '{}'".format(base_snapshot_name))
|
||||
yield from self._control_vm("snapshot", base_snapshot_name)
|
||||
await self._control_vm("snapshot", base_snapshot_name)
|
||||
|
||||
# create the linked clone based on the base snapshot
|
||||
new_vmx_path = os.path.join(self.working_dir, self.name + ".vmx")
|
||||
yield from self._control_vm("clone",
|
||||
await self._control_vm("clone",
|
||||
new_vmx_path,
|
||||
"linked",
|
||||
"-snapshot={}".format(base_snapshot_name),
|
||||
@@ -323,8 +319,7 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
return vnet
|
||||
|
||||
@asyncio.coroutine
|
||||
def _add_ubridge_connection(self, nio, adapter_number):
|
||||
async def _add_ubridge_connection(self, nio, adapter_number):
|
||||
"""
|
||||
Creates a connection in uBridge.
|
||||
|
||||
@@ -333,30 +328,29 @@ class VMwareVM(BaseNode):
|
||||
"""
|
||||
|
||||
vnet = self._get_vnet(adapter_number)
|
||||
yield from self._ubridge_send("bridge create {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge create {name}".format(name=vnet))
|
||||
vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
|
||||
|
||||
if sys.platform.startswith("darwin"):
|
||||
# special case on OSX, we cannot bind VMnet interfaces using the libpcap
|
||||
yield from self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
|
||||
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface))
|
||||
else:
|
||||
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False)
|
||||
yield from self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
|
||||
await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
|
||||
|
||||
if isinstance(nio, NIOUDP):
|
||||
yield from self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=vnet,
|
||||
await self._ubridge_send('bridge add_nio_udp {name} {lport} {rhost} {rport}'.format(name=vnet,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport))
|
||||
|
||||
if nio.capturing:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=vnet, pcap_file=nio.pcap_output_file))
|
||||
await self._ubridge_send('bridge start_capture {name} "{pcap_file}"'.format(name=vnet, pcap_file=nio.pcap_output_file))
|
||||
|
||||
yield from self._ubridge_send('bridge start {name}'.format(name=vnet))
|
||||
yield from self._ubridge_apply_filters(vnet, nio.filters)
|
||||
await self._ubridge_send('bridge start {name}'.format(name=vnet))
|
||||
await self._ubridge_apply_filters(vnet, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _update_ubridge_connection(self, adapter_number, nio):
|
||||
async def _update_ubridge_connection(self, adapter_number, nio):
|
||||
"""
|
||||
Update a connection in uBridge.
|
||||
|
||||
@@ -367,10 +361,9 @@ class VMwareVM(BaseNode):
|
||||
bridge_name = self._get_vnet(adapter_number)
|
||||
except VMwareError:
|
||||
return # vnet not yet available
|
||||
yield from self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
await self._ubridge_apply_filters(bridge_name, nio.filters)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _delete_ubridge_connection(self, adapter_number):
|
||||
async def _delete_ubridge_connection(self, adapter_number):
|
||||
"""
|
||||
Deletes a connection in uBridge.
|
||||
|
||||
@@ -380,10 +373,9 @@ class VMwareVM(BaseNode):
|
||||
vnet = "ethernet{}.vnet".format(adapter_number)
|
||||
if vnet not in self._vmx_pairs:
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge delete {name}".format(name=vnet))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
async def _start_ubridge_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Start a packet capture in uBridge.
|
||||
|
||||
@@ -396,11 +388,10 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
if not self._ubridge_hypervisor:
|
||||
raise VMwareError("Cannot start the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=vnet,
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name=vnet,
|
||||
output_file=output_file))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_ubridge_capture(self, adapter_number):
|
||||
async def _stop_ubridge_capture(self, adapter_number):
|
||||
"""
|
||||
Stop a packet capture in uBridge.
|
||||
|
||||
@@ -412,7 +403,7 @@ class VMwareVM(BaseNode):
|
||||
raise VMwareError("vnet {} not in VMX file".format(vnet))
|
||||
if not self._ubridge_hypervisor:
|
||||
raise VMwareError("Cannot stop the packet capture: uBridge is not running")
|
||||
yield from self._ubridge_send("bridge stop_capture {name}".format(name=vnet))
|
||||
await self._ubridge_send("bridge stop_capture {name}".format(name=vnet))
|
||||
|
||||
def check_hw_virtualization(self):
|
||||
"""
|
||||
@@ -426,8 +417,7 @@ class VMwareVM(BaseNode):
|
||||
return True
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts this VMware VM.
|
||||
"""
|
||||
@@ -435,14 +425,14 @@ class VMwareVM(BaseNode):
|
||||
if self.status == "started":
|
||||
return
|
||||
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise VMwareError("The VM is already running in VMware")
|
||||
|
||||
ubridge_path = self.ubridge_path
|
||||
if not ubridge_path or not os.path.isfile(ubridge_path):
|
||||
raise VMwareError("ubridge is necessary to start a VMware VM")
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
self._read_vmx_file()
|
||||
# check if there is enough RAM to run
|
||||
if "memsize" in self._vmx_pairs:
|
||||
@@ -452,20 +442,20 @@ class VMwareVM(BaseNode):
|
||||
self._write_vmx_file()
|
||||
|
||||
if self._headless:
|
||||
yield from self._control_vm("start", "nogui")
|
||||
await self._control_vm("start", "nogui")
|
||||
else:
|
||||
yield from self._control_vm("start")
|
||||
await self._control_vm("start")
|
||||
|
||||
try:
|
||||
if self._ubridge_hypervisor:
|
||||
for adapter_number in range(0, self._adapters):
|
||||
nio = self._ethernet_adapters[adapter_number].get_nio(0)
|
||||
if nio:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
|
||||
yield from self._start_console()
|
||||
await self._start_console()
|
||||
except VMwareError:
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
raise
|
||||
|
||||
if self._get_vmx_setting("vhv.enable", "TRUE"):
|
||||
@@ -475,25 +465,24 @@ class VMwareVM(BaseNode):
|
||||
self.status = "started"
|
||||
log.info("VMware VM '{name}' [{id}] started".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops this VMware VM.
|
||||
"""
|
||||
|
||||
self._hw_virtualization = False
|
||||
yield from self._stop_remote_console()
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_remote_console()
|
||||
await self._stop_ubridge()
|
||||
|
||||
try:
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
if self.on_close == "save_vm_state":
|
||||
yield from self._control_vm("suspend")
|
||||
await self._control_vm("suspend")
|
||||
elif self.on_close == "shutdown_signal":
|
||||
# use ACPI to shutdown the VM
|
||||
yield from self._control_vm("stop", "soft")
|
||||
await self._control_vm("stop", "soft")
|
||||
else:
|
||||
yield from self._control_vm("stop")
|
||||
await self._control_vm("stop")
|
||||
finally:
|
||||
self._started = False
|
||||
self.status = "stopped"
|
||||
@@ -519,49 +508,45 @@ class VMwareVM(BaseNode):
|
||||
self._vmx_pairs["ethernet{}.startconnected".format(adapter_number)] = "TRUE"
|
||||
self._write_vmx_file()
|
||||
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
log.info("VMware VM '{name}' [{id}] stopped".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def suspend(self):
|
||||
async def suspend(self):
|
||||
"""
|
||||
Suspends this VMware VM.
|
||||
"""
|
||||
|
||||
if self.manager.host_type != "ws":
|
||||
raise VMwareError("Pausing a VM is only supported by VMware Workstation")
|
||||
yield from self._control_vm("pause")
|
||||
await self._control_vm("pause")
|
||||
self.status = "suspended"
|
||||
log.info("VMware VM '{name}' [{id}] paused".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def resume(self):
|
||||
async def resume(self):
|
||||
"""
|
||||
Resumes this VMware VM.
|
||||
"""
|
||||
|
||||
if self.manager.host_type != "ws":
|
||||
raise VMwareError("Unpausing a VM is only supported by VMware Workstation")
|
||||
yield from self._control_vm("unpause")
|
||||
await self._control_vm("unpause")
|
||||
self.status = "started"
|
||||
log.info("VMware VM '{name}' [{id}] resumed".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads this VMware VM.
|
||||
"""
|
||||
|
||||
yield from self._control_vm("reset")
|
||||
await self._control_vm("reset")
|
||||
log.info("VMware VM '{name}' [{id}] reloaded".format(name=self.name, id=self.id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VMware VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
for adapter in self._ethernet_adapters.values():
|
||||
@@ -571,12 +556,12 @@ class VMwareVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
try:
|
||||
self.on_close = "power_off"
|
||||
yield from self.stop()
|
||||
await self.stop()
|
||||
except VMwareError:
|
||||
pass
|
||||
|
||||
if self.linked_clone:
|
||||
yield from self.manager.remove_from_vmware_inventory(self._vmx_path)
|
||||
await self.manager.remove_from_vmware_inventory(self._vmx_path)
|
||||
|
||||
@property
|
||||
def headless(self):
|
||||
@@ -722,8 +707,7 @@ class VMwareVM(BaseNode):
|
||||
log.info("VMware VM '{name}' [{id}] is not allowed to use any adapter".format(name=self.name, id=self.id))
|
||||
self._use_any_adapter = use_any_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_add_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Adds an adapter NIO binding.
|
||||
|
||||
@@ -743,7 +727,7 @@ class VMwareVM(BaseNode):
|
||||
# check for the connection type
|
||||
connection_type = "ethernet{}.connectiontype".format(adapter_number)
|
||||
if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"):
|
||||
if (yield from self.is_running()):
|
||||
if (await self.is_running()):
|
||||
raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. "
|
||||
"Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type],
|
||||
adapter_number=adapter_number,
|
||||
@@ -757,15 +741,14 @@ class VMwareVM(BaseNode):
|
||||
|
||||
adapter.add_nio(0, nio)
|
||||
if self._started and self._ubridge_hypervisor:
|
||||
yield from self._add_ubridge_connection(nio, adapter_number)
|
||||
await self._add_ubridge_connection(nio, adapter_number)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: {nio} added to adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
nio=nio,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
async def adapter_update_nio_binding(self, adapter_number, nio):
|
||||
"""
|
||||
Update a port NIO binding.
|
||||
|
||||
@@ -775,15 +758,14 @@ class VMwareVM(BaseNode):
|
||||
|
||||
if self._ubridge_hypervisor:
|
||||
try:
|
||||
yield from self._update_ubridge_connection(adapter_number, nio)
|
||||
await self._update_ubridge_connection(adapter_number, nio)
|
||||
except IndexError:
|
||||
raise VMwareError('Adapter {adapter_number} does not exist on VMware VM "{name}"'.format(
|
||||
name=self._name,
|
||||
adapter_number=adapter_number
|
||||
))
|
||||
|
||||
@asyncio.coroutine
|
||||
def adapter_remove_nio_binding(self, adapter_number):
|
||||
async def adapter_remove_nio_binding(self, adapter_number):
|
||||
"""
|
||||
Removes an adapter NIO binding.
|
||||
|
||||
@@ -803,7 +785,7 @@ class VMwareVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(nio.lport, self._project)
|
||||
adapter.remove_nio(0)
|
||||
if self._started and self._ubridge_hypervisor:
|
||||
yield from self._delete_ubridge_connection(adapter_number)
|
||||
await self._delete_ubridge_connection(adapter_number)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: {nio} removed from adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
@@ -842,8 +824,7 @@ class VMwareVM(BaseNode):
|
||||
"serial0.startconnected": "TRUE"}
|
||||
self._vmx_pairs.update(serial_port)
|
||||
|
||||
@asyncio.coroutine
|
||||
def _start_console(self):
|
||||
async def _start_console(self):
|
||||
"""
|
||||
Starts remote console support for this VM.
|
||||
"""
|
||||
@@ -851,7 +832,7 @@ class VMwareVM(BaseNode):
|
||||
if self.console and self.console_type == "telnet":
|
||||
pipe_name = self._get_pipe_name()
|
||||
try:
|
||||
self._remote_pipe = yield from asyncio_open_serial(self._get_pipe_name())
|
||||
self._remote_pipe = await asyncio_open_serial(self._get_pipe_name())
|
||||
except OSError as e:
|
||||
raise VMwareError("Could not open serial pipe '{}': {}".format(pipe_name, e))
|
||||
server = AsyncioTelnetServer(reader=self._remote_pipe,
|
||||
@@ -859,18 +840,17 @@ class VMwareVM(BaseNode):
|
||||
binary=True,
|
||||
echo=True)
|
||||
try:
|
||||
self._telnet_server = yield from asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
|
||||
except OSError as e:
|
||||
self.project.emit("log.warning", {"message": "Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e)})
|
||||
|
||||
@asyncio.coroutine
|
||||
def _stop_remote_console(self):
|
||||
async def _stop_remote_console(self):
|
||||
"""
|
||||
Stops remote console support for this VM.
|
||||
"""
|
||||
if self._telnet_server:
|
||||
self._telnet_server.close()
|
||||
yield from self._telnet_server.wait_closed()
|
||||
await self._telnet_server.wait_closed()
|
||||
self._remote_pipe.close()
|
||||
self._telnet_server = None
|
||||
|
||||
@@ -887,8 +867,7 @@ class VMwareVM(BaseNode):
|
||||
|
||||
super(VMwareVM, VMwareVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, adapter_number, output_file):
|
||||
async def start_capture(self, adapter_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -913,13 +892,13 @@ class VMwareVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self._started:
|
||||
yield from self._start_ubridge_capture(adapter_number, output_file)
|
||||
await self._start_ubridge_capture(adapter_number, output_file)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: starting packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
adapter_number=adapter_number))
|
||||
|
||||
def stop_capture(self, adapter_number):
|
||||
async def stop_capture(self, adapter_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -940,7 +919,7 @@ class VMwareVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self._started:
|
||||
yield from self._stop_ubridge_capture(adapter_number)
|
||||
await self._stop_ubridge_capture(adapter_number)
|
||||
|
||||
log.info("VMware VM '{name}' [{id}]: stopping packet capture on adapter {adapter_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
||||
@@ -37,15 +37,14 @@ class VPCS(BaseManager):
|
||||
self._free_mac_ids = {}
|
||||
self._used_mac_ids = {}
|
||||
|
||||
@asyncio.coroutine
|
||||
def create_node(self, *args, **kwargs):
|
||||
async def create_node(self, *args, **kwargs):
|
||||
"""
|
||||
Creates a new VPCS VM.
|
||||
|
||||
:returns: VPCSVM instance
|
||||
"""
|
||||
|
||||
node = yield from super().create_node(*args, **kwargs)
|
||||
node = await super().create_node(*args, **kwargs)
|
||||
self._free_mac_ids.setdefault(node.project.id, list(range(0, 255)))
|
||||
try:
|
||||
self._used_mac_ids[node.id] = self._free_mac_ids[node.project.id].pop(0)
|
||||
@@ -53,8 +52,7 @@ class VPCS(BaseManager):
|
||||
raise VPCSError("Cannot create a new VPCS VM (limit of 255 VMs reached on this host)")
|
||||
return node
|
||||
|
||||
@asyncio.coroutine
|
||||
def close_node(self, node_id, *args, **kwargs):
|
||||
async def close_node(self, node_id, *args, **kwargs):
|
||||
"""
|
||||
Closes a VPCS VM.
|
||||
|
||||
@@ -66,7 +64,7 @@ class VPCS(BaseManager):
|
||||
i = self._used_mac_ids[node_id]
|
||||
self._free_mac_ids[node.project.id].insert(0, i)
|
||||
del self._used_mac_ids[node_id]
|
||||
yield from super().close_node(node_id, *args, **kwargs)
|
||||
await super().close_node(node_id, *args, **kwargs)
|
||||
return node
|
||||
|
||||
def get_mac_id(self, node_id):
|
||||
|
||||
@@ -77,13 +77,12 @@ class VPCSVM(BaseNode):
|
||||
def ethernet_adapter(self):
|
||||
return self._ethernet_adapter
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
async def close(self):
|
||||
"""
|
||||
Closes this VPCS VM.
|
||||
"""
|
||||
|
||||
if not (yield from super().close()):
|
||||
if not (await super().close()):
|
||||
return False
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
@@ -95,15 +94,14 @@ class VPCSVM(BaseNode):
|
||||
self.manager.port_manager.release_udp_port(self._local_udp_tunnel[1].lport, self._project)
|
||||
self._local_udp_tunnel = None
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
|
||||
return True
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_requirements(self):
|
||||
async def _check_requirements(self):
|
||||
"""
|
||||
Check if VPCS is available with the correct version.
|
||||
"""
|
||||
@@ -121,7 +119,7 @@ class VPCSVM(BaseNode):
|
||||
if not os.access(path, os.X_OK):
|
||||
raise VPCSError("VPCS program '{}' is not executable".format(path))
|
||||
|
||||
yield from self._check_vpcs_version()
|
||||
await self._check_vpcs_version()
|
||||
|
||||
def __json__(self):
|
||||
|
||||
@@ -200,13 +198,12 @@ class VPCSVM(BaseNode):
|
||||
except OSError as e:
|
||||
raise VPCSError('Cannot write the startup script file "{}": {}'.format(startup_script_path, e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def _check_vpcs_version(self):
|
||||
async def _check_vpcs_version(self):
|
||||
"""
|
||||
Checks if the VPCS executable version is >= 0.8b or == 0.6.1.
|
||||
"""
|
||||
try:
|
||||
output = yield from subprocess_check_output(self._vpcs_path(), "-v", cwd=self.working_dir)
|
||||
output = await subprocess_check_output(self._vpcs_path(), "-v", cwd=self.working_dir)
|
||||
match = re.search("Welcome to Virtual PC Simulator, version ([0-9a-z\.]+)", output)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
@@ -218,13 +215,12 @@ class VPCSVM(BaseNode):
|
||||
except (OSError, subprocess.SubprocessError) as e:
|
||||
raise VPCSError("Error while looking for the VPCS version: {}".format(e))
|
||||
|
||||
@asyncio.coroutine
|
||||
def start(self):
|
||||
async def start(self):
|
||||
"""
|
||||
Starts the VPCS process.
|
||||
"""
|
||||
|
||||
yield from self._check_requirements()
|
||||
await self._check_requirements()
|
||||
if not self.is_running():
|
||||
nio = self._ethernet_adapter.get_nio(0)
|
||||
command = self._build_command()
|
||||
@@ -237,18 +233,18 @@ class VPCSVM(BaseNode):
|
||||
flags = subprocess.CREATE_NEW_PROCESS_GROUP
|
||||
with open(self._vpcs_stdout_file, "w", encoding="utf-8") as fd:
|
||||
self.command_line = ' '.join(command)
|
||||
self._process = yield from asyncio.create_subprocess_exec(*command,
|
||||
self._process = await asyncio.create_subprocess_exec(*command,
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=self.working_dir,
|
||||
creationflags=flags)
|
||||
monitor_process(self._process, self._termination_callback)
|
||||
|
||||
yield from self._start_ubridge()
|
||||
await self._start_ubridge()
|
||||
if nio:
|
||||
yield from self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
yield from self.start_wrap_console()
|
||||
await self.start_wrap_console()
|
||||
|
||||
log.info("VPCS instance {} started PID={}".format(self.name, self._process.pid))
|
||||
self._started = True
|
||||
@@ -273,18 +269,17 @@ class VPCSVM(BaseNode):
|
||||
if returncode != 0:
|
||||
self.project.emit("log.error", {"message": "VPCS process has stopped, return code: {}\n{}".format(returncode, self.read_vpcs_stdout())})
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop(self):
|
||||
async def stop(self):
|
||||
"""
|
||||
Stops the VPCS process.
|
||||
"""
|
||||
|
||||
yield from self._stop_ubridge()
|
||||
await self._stop_ubridge()
|
||||
if self.is_running():
|
||||
self._terminate_process()
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
yield from wait_for_process_termination(self._process, timeout=3)
|
||||
await wait_for_process_termination(self._process, timeout=3)
|
||||
except asyncio.TimeoutError:
|
||||
if self._process.returncode is None:
|
||||
try:
|
||||
@@ -296,16 +291,15 @@ class VPCSVM(BaseNode):
|
||||
|
||||
self._process = None
|
||||
self._started = False
|
||||
yield from super().stop()
|
||||
await super().stop()
|
||||
|
||||
@asyncio.coroutine
|
||||
def reload(self):
|
||||
async def reload(self):
|
||||
"""
|
||||
Reloads the VPCS process (stop & start).
|
||||
"""
|
||||
|
||||
yield from self.stop()
|
||||
yield from self.start()
|
||||
await self.stop()
|
||||
await self.start()
|
||||
|
||||
def _terminate_process(self):
|
||||
"""
|
||||
@@ -364,8 +358,7 @@ class VPCSVM(BaseNode):
|
||||
|
||||
super(VPCSVM, VPCSVM).console_type.__set__(self, new_console_type)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_add_nio_binding(self, port_number, nio):
|
||||
async def port_add_nio_binding(self, port_number, nio):
|
||||
"""
|
||||
Adds a port NIO binding.
|
||||
|
||||
@@ -378,7 +371,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.add_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
self._ethernet_adapter.add_nio(port_number, nio)
|
||||
log.info('VPCS "{name}" [{id}]: {nio} added to port {port_number}'.format(name=self._name,
|
||||
@@ -388,16 +381,14 @@ class VPCSVM(BaseNode):
|
||||
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_update_nio_binding(self, port_number, nio):
|
||||
async def port_update_nio_binding(self, port_number, nio):
|
||||
if not self._ethernet_adapter.port_exists(port_number):
|
||||
raise VPCSError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=self._ethernet_adapter,
|
||||
port_number=port_number))
|
||||
if self.is_running():
|
||||
yield from self.update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
await self.update_ubridge_udp_connection("VPCS-{}".format(self._id), self._local_udp_tunnel[1], nio)
|
||||
|
||||
@asyncio.coroutine
|
||||
def port_remove_nio_binding(self, port_number):
|
||||
async def port_remove_nio_binding(self, port_number):
|
||||
"""
|
||||
Removes a port NIO binding.
|
||||
|
||||
@@ -411,7 +402,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
|
||||
if self.is_running():
|
||||
yield from self._ubridge_send("bridge delete {name}".format(name="VPCS-{}".format(self._id)))
|
||||
await self._ubridge_send("bridge delete {name}".format(name="VPCS-{}".format(self._id)))
|
||||
|
||||
nio = self._ethernet_adapter.get_nio(port_number)
|
||||
if isinstance(nio, NIOUDP):
|
||||
@@ -424,8 +415,7 @@ class VPCSVM(BaseNode):
|
||||
port_number=port_number))
|
||||
return nio
|
||||
|
||||
@asyncio.coroutine
|
||||
def start_capture(self, port_number, output_file):
|
||||
async def start_capture(self, port_number, output_file):
|
||||
"""
|
||||
Starts a packet capture.
|
||||
|
||||
@@ -448,15 +438,14 @@ class VPCSVM(BaseNode):
|
||||
nio.startPacketCapture(output_file)
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VPCS-{}".format(self._id),
|
||||
await self._ubridge_send('bridge start_capture {name} "{output_file}"'.format(name="VPCS-{}".format(self._id),
|
||||
output_file=output_file))
|
||||
|
||||
log.info("VPCS '{name}' [{id}]: starting packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
port_number=port_number))
|
||||
|
||||
@asyncio.coroutine
|
||||
def stop_capture(self, port_number):
|
||||
async def stop_capture(self, port_number):
|
||||
"""
|
||||
Stops a packet capture.
|
||||
|
||||
@@ -475,7 +464,7 @@ class VPCSVM(BaseNode):
|
||||
nio.stopPacketCapture()
|
||||
|
||||
if self.ubridge:
|
||||
yield from self._ubridge_send('bridge stop_capture {name}'.format(name="VPCS-{}".format(self._id)))
|
||||
await self._ubridge_send('bridge stop_capture {name}'.format(name="VPCS-{}".format(self._id)))
|
||||
|
||||
log.info("VPCS '{name}' [{id}]: stopping packet capture on port {port_number}".format(name=self.name,
|
||||
id=self.id,
|
||||
|
||||
Reference in new issue
Block a user