Rename modules => hypervisor

This commit is contained in:
Julien Duponchelle committed 2016-03-08 16:12:46 +01:00
1 parent 6fa2491255
commit 3296b97f59
145 files changed
+308 -307

No files matched your search

+734
View File
@@ -0,0 +1,734 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Dynamips server module.
"""
import aiohttp
import sys
import os
import shutil
import socket
import time
import asyncio
import tempfile
import logging
import glob
log = logging.getLogger(__name__)
from gns3server.utils.interfaces import get_windows_interfaces, is_interface_up
from gns3server.utils.asyncio import wait_run_in_executor
from pkg_resources import parse_version
from uuid import UUID, uuid4
from ..base_manager import BaseManager
from ..project_manager import ProjectManager
from ..port_manager import PortManager
from .dynamips_error import DynamipsError
from .hypervisor import Hypervisor
from .nodes.router import Router
from .dynamips_vm import DynamipsVM
from .dynamips_device import DynamipsDevice
# NIOs
from .nios.nio_udp import NIOUDP
from .nios.nio_unix import NIOUNIX
from .nios.nio_vde import NIOVDE
from .nios.nio_tap import NIOTAP
from .nios.nio_generic_ethernet import NIOGenericEthernet
from .nios.nio_linux_ethernet import NIOLinuxEthernet
from .nios.nio_null import NIONull
# Adapters
from .adapters.c7200_io_2fe import C7200_IO_2FE
from .adapters.c7200_io_fe import C7200_IO_FE
from .adapters.c7200_io_ge_e import C7200_IO_GE_E
from .adapters.nm_16esw import NM_16ESW
from .adapters.nm_1e import NM_1E
from .adapters.nm_1fe_tx import NM_1FE_TX
from .adapters.nm_4e import NM_4E
from .adapters.nm_4t import NM_4T
from .adapters.pa_2fe_tx import PA_2FE_TX
from .adapters.pa_4e import PA_4E
from .adapters.pa_4t import PA_4T
from .adapters.pa_8e import PA_8E
from .adapters.pa_8t import PA_8T
from .adapters.pa_a1 import PA_A1
from .adapters.pa_fe_tx import PA_FE_TX
from .adapters.pa_ge import PA_GE
from .adapters.pa_pos_oc3 import PA_POS_OC3
from .adapters.wic_1enet import WIC_1ENET
from .adapters.wic_1t import WIC_1T
from .adapters.wic_2t import WIC_2T
ADAPTER_MATRIX = {"C7200-IO-2FE": C7200_IO_2FE,
"C7200-IO-FE": C7200_IO_FE,
"C7200-IO-GE-E": C7200_IO_GE_E,
"NM-16ESW": NM_16ESW,
"NM-1E": NM_1E,
"NM-1FE-TX": NM_1FE_TX,
"NM-4E": NM_4E,
"NM-4T": NM_4T,
"PA-2FE-TX": PA_2FE_TX,
"PA-4E": PA_4E,
"PA-4T+": PA_4T,
"PA-8E": PA_8E,
"PA-8T": PA_8T,
"PA-A1": PA_A1,
"PA-FE-TX": PA_FE_TX,
"PA-GE": PA_GE,
"PA-POS-OC3": PA_POS_OC3}
WIC_MATRIX = {"WIC-1ENET": WIC_1ENET,
"WIC-1T": WIC_1T,
"WIC-2T": WIC_2T}
class Dynamips(BaseManager):
_VM_CLASS = DynamipsVM
_DEVICE_CLASS = DynamipsDevice
_ghost_ios_lock = None
def __init__(self):
super().__init__()
Dynamips._ghost_ios_lock = asyncio.Lock()
self._devices = {}
self._ghost_files = set()
self._dynamips_path = None
self._dynamips_ids = {}
def get_dynamips_id(self, project_id):
"""
:param project_id: UUID of the project
:returns: a free dynamips id
"""
self._dynamips_ids.setdefault(project_id, set())
dynamips_id = 0
for dynamips_id in range(1, 4097):
if dynamips_id not in self._dynamips_ids[project_id]:
self._dynamips_ids[project_id].add(dynamips_id)
return dynamips_id
raise DynamipsError("Maximum number of Dynamips instances reached")
def take_dynamips_id(self, project_id, dynamips_id):
"""
Reserve a dynamips id or raise an error
:param project_id: UUID of the project
:param dynamips_id: Asked id
"""
self._dynamips_ids.setdefault(project_id, set())
if dynamips_id in self._dynamips_ids[project_id]:
raise DynamipsError("Dynamips identifier {} is already used by another router".format(dynamips_id))
self._dynamips_ids[project_id].add(dynamips_id)
def release_dynamips_id(self, project_id, dynamips_id):
"""
A dynamips id can be reused by another vm
:param project_id: UUID of the project
:param dynamips_id: Asked id
"""
self._dynamips_ids.setdefault(project_id, set())
if dynamips_id in self._dynamips_ids[project_id]:
self._dynamips_ids[project_id].remove(dynamips_id)
@asyncio.coroutine
def unload(self):
yield from BaseManager.unload(self)
tasks = []
for device in self._devices.values():
tasks.append(asyncio.async(device.hypervisor.stop()))
if tasks:
done, _ = yield from asyncio.wait(tasks)
for future in done:
try:
future.result()
except (Exception, GeneratorExit) as e:
log.error("Could not stop device hypervisor {}".format(e), exc_info=1)
continue
@asyncio.coroutine
def project_closing(self, project):
"""
Called when a project is about to be closed.
:param project: Project instance
"""
yield from 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.async(device.delete()))
if tasks:
done, _ = yield from 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):
"""
Called when a project is closed.
:param project: Project instance
"""
yield from super().project_closed(project)
# delete useless Dynamips files
project_dir = project.module_working_path(self.module_name.lower())
files = glob.glob(os.path.join(glob.escape(project_dir), "*.ghost"))
files += glob.glob(os.path.join(glob.escape(project_dir), "*_lock"))
files += glob.glob(os.path.join(glob.escape(project_dir), "ilt_*"))
files += glob.glob(os.path.join(glob.escape(project_dir), "c[0-9][0-9][0-9][0-9]_i[0-9]*_rommon_vars"))
files += glob.glob(os.path.join(glob.escape(project_dir), "c[0-9][0-9][0-9][0-9]_i[0-9]*_log.txt"))
for file in files:
try:
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)
except OSError as e:
log.warn("Could not delete file {}: {}".format(file, e))
continue
# Release the dynamips ids if we want to reload the same project
# later
if project.id in self._dynamips_ids:
del self._dynamips_ids[project.id]
@asyncio.coroutine
def project_moved(self, project):
"""
Called when a project is moved.
:param project: Project instance
"""
for vm in self._vms.values():
if vm.project.id == project.id:
yield from vm.hypervisor.set_working_dir(project.module_working_directory(self.module_name.lower()))
for device in self._devices.values():
if device.project.id == project.id:
yield from device.hypervisor.set_working_dir(project.module_working_directory(self.module_name.lower()))
@asyncio.coroutine
def project_committed(self, project):
"""
Called when a project has been committed.
:param project: Project instance
"""
# save the configs when the project is committed
for vm in self._vms.copy().values():
if vm.project.id == project.id:
try:
yield from vm.save_configs()
except DynamipsError as e:
log.warning(e)
continue
@property
def dynamips_path(self):
"""
Returns the path to Dynamips.
:returns: path
"""
return self._dynamips_path
@asyncio.coroutine
def create_device(self, name, project_id, device_id, device_type, *args, **kwargs):
"""
Create a new Dynamips device.
:param name: Device name
:param project_id: Project identifier
:param vm_id: restore a VM identifier
"""
project = ProjectManager.instance().get_project(project_id)
if device_id and isinstance(device_id, int):
with (yield from BaseManager._convert_lock):
device_id = yield from self.convert_old_project(project, device_id, name)
if not device_id:
device_id = str(uuid4())
device = self._DEVICE_CLASS(name, device_id, project, self, device_type, *args, **kwargs)
yield from device.create()
self._devices[device.id] = device
return device
def get_device(self, device_id, project_id=None):
"""
Returns a device instance.
:param device_id: Device identifier
:param project_id: Project identifier
:returns: Device instance
"""
if project_id:
# check the project_id exists
project = ProjectManager.instance().get_project(project_id)
try:
UUID(device_id, version=4)
except ValueError:
raise aiohttp.web.HTTPBadRequest(text="Device ID} is not a valid UUID".format(device_id))
if device_id not in self._devices:
raise aiohttp.web.HTTPNotFound(text="Device ID {} doesn't exist".format(device_id))
device = self._devices[device_id]
if project_id:
if device.project.id != project.id:
raise aiohttp.web.HTTPNotFound(text="Project ID {} doesn't belong to device {}".format(project_id, device.name))
return device
@asyncio.coroutine
def delete_device(self, device_id):
"""
Delete a device
:param device_id: Device identifier
:returns: Device instance
"""
device = self.get_device(device_id)
yield from device.delete()
del self._devices[device.id]
return device
def find_dynamips(self):
# look for Dynamips
dynamips_path = self.config.get_section_config("Dynamips").get("dynamips_path")
if not dynamips_path:
dynamips_path = shutil.which("dynamips")
if not dynamips_path:
raise DynamipsError("Could not find Dynamips")
if not os.path.isfile(dynamips_path):
raise DynamipsError("Dynamips {} is not accessible".format(dynamips_path))
if not os.access(dynamips_path, os.X_OK):
raise DynamipsError("Dynamips is not executable")
self._dynamips_path = dynamips_path
return dynamips_path
@asyncio.coroutine
def start_new_hypervisor(self, working_dir=None):
"""
Creates a new Dynamips process and start it.
:param working_dir: working directory
:returns: the new hypervisor instance
"""
if not self._dynamips_path:
self.find_dynamips()
if not working_dir:
working_dir = tempfile.gettempdir()
# FIXME: hypervisor should always listen to 127.0.0.1
# See https://github.com/GNS3/dynamips/issues/62
server_config = self.config.get_section_config("Server")
server_host = server_config.get("host")
try:
info = socket.getaddrinfo(server_host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
if not info:
raise DynamipsError("getaddrinfo returns an empty list on {}".format(server_host))
for res in info:
af, socktype, proto, _, sa = res
# let the OS find an unused port for the Dynamips hypervisor
with socket.socket(af, socktype, proto) as sock:
sock.bind(sa)
port = sock.getsockname()[1]
break
except OSError as e:
raise DynamipsError("Could not find free port for the Dynamips hypervisor: {}".format(e))
port_manager = PortManager.instance()
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()
log.info("Hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port))
yield from 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):
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):
try:
yield from 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):
"""
Creates a new NIO.
:param node: Dynamips node instance
:param nio_settings: information to create the NIO
:returns: a NIO object
"""
nio = None
if nio_settings["type"] == "nio_udp":
lport = nio_settings["lport"]
rhost = nio_settings["rhost"]
rport = nio_settings["rport"]
try:
info = socket.getaddrinfo(rhost, rport, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)
if not info:
raise DynamipsError("getaddrinfo returns an empty list on {}:{}".format(rhost, rport))
for res in info:
af, socktype, proto, _, sa = res
with socket.socket(af, socktype, proto) as sock:
sock.connect(sa)
except OSError as e:
raise DynamipsError("Could not create an UDP connection to {}:{}: {}".format(rhost, rport, e))
nio = NIOUDP(node.hypervisor, lport, rhost, rport)
elif nio_settings["type"] == "nio_generic_ethernet":
ethernet_device = nio_settings["ethernet_device"]
if sys.platform.startswith("win"):
# replace the interface name by the GUID on Windows
interfaces = get_windows_interfaces()
npf_interface = None
for interface in interfaces:
if interface["name"] == ethernet_device:
npf_interface = interface["id"]
if not npf_interface:
raise DynamipsError("Could not find interface {} on this host".format(ethernet_device))
else:
ethernet_device = npf_interface
if not is_interface_up(ethernet_device):
raise aiohttp.web.HTTPConflict(text="Ethernet interface {} is down".format(ethernet_device))
nio = NIOGenericEthernet(node.hypervisor, ethernet_device)
elif nio_settings["type"] == "nio_linux_ethernet":
if sys.platform.startswith("win"):
raise DynamipsError("This NIO type is not supported on Windows")
ethernet_device = nio_settings["ethernet_device"]
nio = NIOLinuxEthernet(node.hypervisor, ethernet_device)
elif nio_settings["type"] == "nio_tap":
tap_device = nio_settings["tap_device"]
nio = NIOTAP(node.hypervisor, tap_device)
if not is_interface_up(tap_device):
# test after the TAP interface has been created (if it doesn't exist yet)
raise aiohttp.web.HTTPConflict(text="TAP interface {} is down".format(tap_device))
elif nio_settings["type"] == "nio_unix":
local_file = nio_settings["local_file"]
remote_file = nio_settings["remote_file"]
nio = NIOUNIX(node.hypervisor, local_file, remote_file)
elif nio_settings["type"] == "nio_vde":
control_file = nio_settings["control_file"]
local_file = nio_settings["local_file"]
nio = NIOVDE(node.hypervisor, control_file, local_file)
elif nio_settings["type"] == "nio_null":
nio = NIONull(node.hypervisor)
else:
raise aiohttp.web.HTTPConflict(text="NIO of type {} is not supported".format(nio_settings["type"]))
yield from nio.create()
return nio
@asyncio.coroutine
def _set_ghost_ios(self, vm):
"""
Manages Ghost IOS support.
:param vm: VM instance
"""
if not vm.mmap:
raise DynamipsError("mmap support is required to enable ghost IOS support")
if vm.platform == "c7200" and vm.npe == "npe-g2":
log.warning("Ghost IOS is not supported for c7200 with NPE-G2")
return
ghost_file = vm.formatted_ghost_file()
ghost_file_path = os.path.join(vm.hypervisor.working_dir, ghost_file)
if ghost_file_path not in self._ghost_files:
# create a new ghost IOS instance
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)
yield from ghost.set_ram(vm.ram)
try:
yield from ghost.start()
yield from ghost.stop()
self._ghost_files.add(ghost_file_path)
except DynamipsError:
raise
finally:
yield from ghost.clean_delete()
except DynamipsError as e:
log.warn("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)
@asyncio.coroutine
def update_vm_settings(self, vm, settings):
"""
Updates the VM settings.
:param vm: VM instance
:param settings: settings to update (dict)
"""
for name, value in settings.items():
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)
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)
if not isinstance(vm.slots[slot_id], type(adapter)):
yield from 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:
slot_id = int(name[-1])
try:
if vm.slots[slot_id]:
yield from 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:
wic_slot_id = int(name[-1])
wic_name = value
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)
if not isinstance(vm.slots[0].wics[wic_slot_id], type(wic)):
yield from 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:
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)
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)
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)
# update the configs if needed
yield from self.set_vm_configs(vm, settings)
@asyncio.coroutine
def set_vm_configs(self, vm, settings):
"""
Set VM configs from pushed content or existing config files.
:param vm: VM instance
:param settings: VM settings
"""
module_workdir = vm.project.module_working_directory(self.module_name.lower())
default_startup_config_path = os.path.join(module_workdir, "configs", "i{}_startup-config.cfg".format(vm.dynamips_id))
default_private_config_path = os.path.join(module_workdir, "configs", "i{}_private-config.cfg".format(vm.dynamips_id))
startup_config_path = settings.get("startup_config")
startup_config_content = settings.get("startup_config_content")
if startup_config_path:
yield from vm.set_configs(startup_config_path)
elif startup_config_content:
startup_config_path = self._create_config(vm, default_startup_config_path, startup_config_content)
yield from vm.set_configs(startup_config_path)
# An empty startup config crash dynamips
else:
startup_config_path = self._create_config(vm, default_startup_config_path, "!\n")
yield from vm.set_configs(startup_config_path)
private_config_path = settings.get("private_config")
private_config_content = settings.get("private_config_content")
if private_config_path:
yield from vm.set_configs(vm.startup_config, private_config_path)
elif private_config_content:
private_config_path = self._create_config(vm, default_private_config_path, private_config_content)
yield from vm.set_configs(vm.startup_config, private_config_path)
def _create_config(self, vm, path, content=None):
"""
Creates a config file.
:param vm: VM instance
:param path: path to the destination config file
:param content: config content
:returns: relative path to the created config file
"""
log.info("Creating config file {}".format(path))
config_dir = os.path.dirname(path)
try:
os.makedirs(config_dir, exist_ok=True)
except OSError as e:
raise DynamipsError("Could not create Dynamips configs directory: {}".format(e))
try:
with open(path, "wb") as f:
if content:
content = "!\n" + content.replace("\r", "")
content = content.replace('%h', vm.name)
f.write(content.encode("utf-8"))
except OSError as e:
raise DynamipsError("Could not create config file {}: {}".format(path, e))
return os.path.join("configs", os.path.basename(path))
@asyncio.coroutine
def auto_idlepc(self, vm):
"""
Try to find the best possible idle-pc value.
:param vm: VM instance
"""
yield from vm.set_idlepc("0x0")
was_auto_started = False
try:
status = yield from vm.get_status()
if status != "running":
yield from vm.start()
was_auto_started = True
yield from asyncio.sleep(20) # leave time to the router to boot
validated_idlepc = None
idlepcs = yield from vm.get_idle_pc_prop()
if not idlepcs:
raise DynamipsError("No Idle-PC values found")
for idlepc in idlepcs:
yield from 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()
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
elapsed_time = time.time() - start_time
cpu_usage = yield from 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:
cpu_usage = 100
log.debug("Auto Idle-PC: CPU usage is {}% after {:.2} seconds".format(cpu_usage, elapsed_time))
if cpu_usage < 70:
validated_idlepc = vm.idlepc
log.debug("Auto Idle-PC: idle-PC value {} has been validated".format(validated_idlepc))
break
if validated_idlepc is None:
raise DynamipsError("Sorry, no idle-pc value was suitable")
except DynamipsError:
raise
finally:
if was_auto_started:
yield from vm.stop()
return validated_idlepc
def get_images_directory(self):
"""
Return the full path of the images directory on disk
"""
return os.path.join(os.path.expanduser(self.config.get_section_config("Server").get("images_path", "~/GNS3/images")), "IOS")
@asyncio.coroutine
def list_images(self):
"""
Return the list of available IOS images.
:returns: Array of hash
"""
image_dir = self.get_images_directory()
if not os.path.exists(image_dir):
return []
try:
files = os.listdir(image_dir)
except OSError as e:
raise DynamipsError("Can not list {}: {}".format(image_dir, str(e)))
files.sort()
images = []
for filename in files:
if filename[0] != "." and not filename.endswith(".md5sum"):
try:
path = os.path.join(image_dir, filename)
with open(path, "rb") as f:
# read the first 7 bytes of the file.
elf_header_start = f.read(7)
except OSError as e:
continue
# valid IOS images must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
if elf_header_start == b'\x7fELF\x01\x02\x01':
path = os.path.relpath(path, image_dir)
images.append({"filename": filename, "path": path})
return images
Whitespace-only changes.
@@ -0,0 +1,166 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class Adapter(object):
"""
Base class for adapters.
:param interfaces: number of interfaces supported by this adapter.
:param wics: number of wics supported by this adapter.
"""
def __init__(self, interfaces=0, wics=0):
self._interfaces = interfaces
self._ports = {}
for port_number in range(0, interfaces):
self._ports[port_number] = None
self._wics = wics * [None]
def removable(self):
"""
Returns True if the adapter can be removed from a slot
and False if not.
:returns: boolean
"""
return True
def port_exists(self, port_number):
"""
Checks if a port exists on this adapter.
:returns: True is the port exists,
False otherwise.
"""
if port_number in self._ports:
return True
return False
def wic_slot_available(self, wic_slot_id):
"""
Checks if a WIC slot is available
:returns: True is the WIC slot is available,
False otherwise.
"""
if self._wics[wic_slot_id] is None:
return True
return False
def install_wic(self, wic_slot_id, wic):
"""
Installs a WIC on this adapter.
:param wic_slot_id: WIC slot ID (integer)
:param wic: WIC instance
"""
self._wics[wic_slot_id] = wic
# Dynamips WICs ports start on a multiple of 16 + port number
# WIC1 port 1 = 16, WIC1 port 2 = 17
# WIC2 port 1 = 32, WIC2 port 2 = 33
# WIC3 port 1 = 48, WIC3 port 2 = 49
base = 16 * (wic_slot_id + 1)
for wic_port in range(0, wic.interfaces):
port_number = base + wic_port
self._ports[port_number] = None
def uninstall_wic(self, wic_slot_id):
"""
Removes a WIC from this adapter.
:param wic_slot_id: WIC slot ID (integer)
"""
wic = self._wics[wic_slot_id]
# Dynamips WICs ports start on a multiple of 16 + port number
# WIC1 port 1 = 16, WIC1 port 2 = 17
# WIC2 port 1 = 32, WIC2 port 2 = 33
# WIC3 port 1 = 48, WIC3 port 2 = 49
base = 16 * (wic_slot_id + 1)
for wic_port in range(0, wic.interfaces):
port_number = base + wic_port
del self._ports[port_number]
self._wics[wic_slot_id] = None
def add_nio(self, port_number, nio):
"""
Adds a NIO to a port on this adapter.
:param port_number: port number (integer)
:param nio: NIO instance
"""
self._ports[port_number] = nio
def remove_nio(self, port_number):
"""
Removes a NIO from a port on this adapter.
:param port_number: port number (integer)
"""
self._ports[port_number] = None
def get_nio(self, port_number):
"""
Returns the NIO assigned to a port.
:params port_number: port number (integer)
:returns: NIO instance
"""
return self._ports[port_number]
@property
def ports(self):
"""
Returns port to NIO mapping
:returns: dictionary port -> NIO
"""
return self._ports
@property
def interfaces(self):
"""
Returns the number of interfaces supported by this adapter.
:returns: number of interfaces
"""
return self._interfaces
@property
def wics(self):
"""
Returns the wics adapters inserted in this adapter.
:returns: list WIC instances
"""
return self._wics
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C1700_MB_1FE(Adapter):
"""
Integrated 1 port FastEthernet adapter for c1700 platform.
"""
def __init__(self):
super().__init__(interfaces=1, wics=2)
def __str__(self):
return "C1700-MB-1FE"
def removable(self):
return False
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C1700_MB_WIC1(Adapter):
"""
Fake module to provide a placeholder for slot 1 interfaces when WICs
are inserted into WIC slot 1.
"""
def __init__(self):
super().__init__(interfaces=0, wics=2)
def __str__(self):
return "C1700-MB-WIC1"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C2600_MB_1E(Adapter):
"""
Integrated 1 port Ethernet adapter for the c2600 platform.
"""
def __init__(self):
super().__init__(interfaces=1, wics=3)
def __str__(self):
return "C2600-MB-1E"
def removable(self):
return False
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C2600_MB_1FE(Adapter):
"""
Integrated 1 port FastEthernet adapter for the c2600 platform.
"""
def __init__(self):
super().__init__(interfaces=1, wics=3)
self._interfaces = 1
def __str__(self):
return "C2600-MB-1FE"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C2600_MB_2E(Adapter):
"""
Integrated 2 port Ethernet adapter for the c2600 platform.
"""
def __init__(self):
super().__init__(interfaces=2, wics=3)
def __str__(self):
return "C2600-MB-2E"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C2600_MB_2FE(Adapter):
"""
Integrated 2 port FastEthernet adapter for the c2600 platform.
"""
def __init__(self):
super().__init__(interfaces=2, wics=3)
def __str__(self):
return "C2600-MB-2FE"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C7200_IO_2FE(Adapter):
"""
C7200-IO-2FE FastEthernet Input/Ouput controller.
"""
def __init__(self):
super().__init__(interfaces=2)
def __str__(self):
return "C7200-IO-2FE"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C7200_IO_FE(Adapter):
"""
C7200-IO-FE FastEthernet Input/Ouput controller.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "C7200-IO-FE"
def removable(self):
return False
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class C7200_IO_GE_E(Adapter):
"""
C7200-IO-GE-E GigabitEthernet Input/Ouput controller.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "C7200-IO-GE-E"
def removable(self):
return False
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class GT96100_FE(Adapter):
def __init__(self):
super().__init__(interfaces=2, wics=3)
def __str__(self):
return "GT96100-FE"
def removable(self):
return False
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class Leopard_2FE(Adapter):
"""
Integrated 2 port FastEthernet adapter for c3660 router.
"""
def __init__(self):
super().__init__(interfaces=2)
self._interfaces = 2
def __str__(self):
return "Leopard-2FE"
def removable(self):
return False
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class NM_16ESW(Adapter):
"""
NM-16ESW FastEthernet network module.
"""
def __init__(self):
super().__init__(interfaces=16)
def __str__(self):
return "NM-16ESW"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class NM_1E(Adapter):
"""
NM-1E Ethernet network module.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "NM-1E"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class NM_1FE_TX(Adapter):
"""
NM-1FE-TX FastEthernet network module.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "NM-1FE-TX"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class NM_4E(Adapter):
"""
NM-4E Ethernet network module.
"""
def __init__(self):
super().__init__(interfaces=4)
def __str__(self):
return "NM-4E"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class NM_4T(Adapter):
"""
NM-4T Serial network module.
"""
def __init__(self):
super().__init__(interfaces=4)
def __str__(self):
return "NM-4T"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_2FE_TX(Adapter):
"""
PA-2FE-TX FastEthernet port adapter.
"""
def __init__(self):
super().__init__(interfaces=2)
def __str__(self):
return "PA-2FE-TX"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_4E(Adapter):
"""
PA-4E Ethernet port adapter.
"""
def __init__(self):
super().__init__(interfaces=4)
def __str__(self):
return "PA-4E"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_4T(Adapter):
"""
PA-4T+ Serial port adapter.
"""
def __init__(self):
super().__init__(interfaces=4)
def __str__(self):
return "PA-4T+"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_8E(Adapter):
"""
PA-8E Ethernet port adapter.
"""
def __init__(self):
super().__init__(interfaces=8)
def __str__(self):
return "PA-8E"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_8T(Adapter):
"""
PA-8T Serial port adapter.
"""
def __init__(self):
super().__init__(interfaces=8)
def __str__(self):
return "PA-8T"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_A1(Adapter):
"""
PA-A1 ATM port adapter.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "PA-A1"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_FE_TX(Adapter):
"""
PA-FE-TX FastEthernet port adapter.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "PA-FE-TX"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_GE(Adapter):
"""
PA-GE GigabitEthernet port adapter.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "PA-GE"
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .adapter import Adapter
class PA_POS_OC3(Adapter):
"""
PA-POS-OC3 port adapter.
"""
def __init__(self):
super().__init__(interfaces=1)
def __str__(self):
return "PA-POS-OC3"
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class WIC_1ENET(object):
"""
WIC-1ENET Ethernet
"""
def __init__(self):
self._interfaces = 2
def __str__(self):
return "WIC-1ENET"
@property
def interfaces(self):
"""
Returns the number of interfaces supported by this WIC.
:returns: number of interfaces
"""
return self._interfaces
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class WIC_1T(object):
"""
WIC-1T Serial
"""
def __init__(self):
self._interfaces = 1
def __str__(self):
return "WIC-1T"
@property
def interfaces(self):
"""
Returns the number of interfaces supported by this WIC.
:returns: number of interfaces
"""
return self._interfaces
@@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class WIC_2T(object):
"""
WIC-2T Serial
"""
def __init__(self):
self._interfaces = 2
def __str__(self):
return "WIC-2T"
@property
def interfaces(self):
"""
Returns the number of interfaces supported by this WIC.
:returns: number of interfaces
"""
return self._interfaces
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .dynamips_error import DynamipsError
from .nodes.atm_switch import ATMSwitch
from .nodes.ethernet_switch import EthernetSwitch
from .nodes.ethernet_hub import EthernetHub
from .nodes.frame_relay_switch import FrameRelaySwitch
import logging
log = logging.getLogger(__name__)
DEVICES = {'atm_switch': ATMSwitch,
'frame_relay_switch': FrameRelaySwitch,
'ethernet_switch': EthernetSwitch,
'ethernet_hub': EthernetHub}
class DynamipsDevice:
"""
Factory to create an Device object based on the type
"""
def __new__(cls, name, device_id, project, manager, device_type, **kwargs):
if device_type not in DEVICES:
raise DynamipsError("Unknown device type: {}".format(device_type))
return DEVICES[device_type](name, device_id, project, manager, **kwargs)
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Custom exceptions for the Dynamips module.
"""
from ..vm_error import VMError
class DynamipsError(VMError):
pass
@@ -0,0 +1,329 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips hypervisor management module ("hypervisor")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L46
"""
import re
import time
import logging
import asyncio
from .dynamips_error import DynamipsError
log = logging.getLogger(__name__)
class DynamipsHypervisor:
"""
Creates a new connection to a Dynamips server (also called hypervisor)
:param working_dir: working directory
:param host: the hostname or ip address string of the Dynamips server
:param port: the tcp port integer (defaults to 7200)
:param timeout: timeout integer for how long to wait for a response to commands sent to the
hypervisor (defaults to 30 seconds)
"""
# Used to parse Dynamips response codes
error_re = re.compile(r"""^2[0-9]{2}-""")
success_re = re.compile(r"""^1[0-9]{2}\s{1}""")
def __init__(self, working_dir, host, port=7200, timeout=30.0):
self._host = host
self._port = port
self._devices = []
self._working_dir = working_dir
self._version = "N/A"
self._timeout = timeout
self._uuid = None
self._reader = None
self._writer = None
self._io_lock = asyncio.Lock()
@asyncio.coroutine
def connect(self, timeout=10):
"""
Connects to the hypervisor.
"""
# connect to a local address by default
# if listening to all addresses (IPv4 or IPv6)
if self._host == "0.0.0.0":
host = "127.0.0.1"
elif self._host == "::":
host = "::1"
else:
host = self._host
begin = time.time()
connection_success = False
last_exception = None
while time.time() - begin < timeout:
yield from asyncio.sleep(0.01)
try:
self._reader, self._writer = yield from asyncio.wait_for(asyncio.open_connection(host, self._port), timeout=1)
except (asyncio.TimeoutError, OSError) as e:
last_exception = e
continue
connection_success = True
break
if not connection_success:
raise DynamipsError("Couldn't connect to hypervisor on {}:{} :{}".format(host, self._port, last_exception))
else:
log.info("Connected to Dynamips hypervisor after {:.4f} seconds".format(time.time() - begin))
try:
version = yield from self.send("hypervisor version")
self._version = version[0].split("-", 1)[0]
except IndexError:
self._version = "Unknown"
self._uuid = yield from self.send("hypervisor uuid")
# this forces to send the working dir to Dynamips
yield from self.set_working_dir(self._working_dir)
@property
def version(self):
"""
Returns Dynamips version.
:returns: version string
"""
return self._version
@asyncio.coroutine
def close(self):
"""
Closes the connection to this hypervisor (but leave it running).
"""
yield from self.send("hypervisor close")
self._writer.close()
self._reader, self._writer = None
@asyncio.coroutine
def stop(self):
"""
Stops this hypervisor (will no longer run).
"""
try:
# try to properly stop the hypervisor
yield from self.send("hypervisor stop")
except DynamipsError:
pass
try:
if self._writer is not None:
yield from 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):
"""
Resets this hypervisor (used to get an empty configuration).
"""
yield from self.send("hypervisor reset")
@asyncio.coroutine
def set_working_dir(self, working_dir):
"""
Sets the working directory for this hypervisor.
:param working_dir: path to the working directory
"""
# encase working_dir in quotes to protect spaces in the path
yield from self.send('hypervisor working_dir "{}"'.format(working_dir))
self._working_dir = working_dir
log.debug("Working directory set to {}".format(self._working_dir))
@property
def working_dir(self):
"""
Returns current working directory
:returns: path to the working directory
"""
return self._working_dir
@property
def uuid(self):
"""
Returns this hypervisor UUID.
:Returns: uuid string
"""
return self._uuid
@property
def devices(self):
"""
Returns the list of devices managed by this hypervisor instance.
:returns: a list of device instances
"""
return self._devices
@property
def port(self):
"""
Returns the port used to start the hypervisor.
:returns: port number (integer)
"""
return self._port
@port.setter
def port(self, port):
"""
Sets the port used to start the hypervisor.
:param port: port number (integer)
"""
self._port = port
@property
def host(self):
"""
Returns the host (binding) used to start the hypervisor.
:returns: host/address (string)
"""
return self._host
@host.setter
def host(self, host):
"""
Sets the host (binding) used to start the hypervisor.
:param host: host/address (string)
"""
self._host = host
@asyncio.coroutine
def send(self, command):
"""
Sends commands to this hypervisor.
:param command: a Dynamips hypervisor command
:returns: results as a list
"""
# Dynamips responses are of the form:
# 1xx yyyyyy\r\n
# 1xx yyyyyy\r\n
# ...
# 100-yyyy\r\n
# or
# 2xx-yyyy\r\n
#
# Where 1xx is a code from 100-199 for a success or 200-299 for an error
# The result might be multiple lines and might be less than the buffer size
# 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):
if self._writer is None or self._reader is None:
raise DynamipsError("Not connected")
try:
command = command.strip() + '\n'
log.debug("sending {}".format(command))
self._writer.write(command.encode())
yield from 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()))
# Now retrieve the result
data = []
buf = ''
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
except asyncio.CancelledError:
# task has been canceled but continue to read
# any remaining data sent by the hypervisor
continue
except ConnectionResetError as e:
# Sometimes WinError 64 (ERROR_NETNAME_DELETED) is returned here on Windows.
# These happen if connection reset is received before IOCP could complete
# a previous operation. Ignore and try again....
log.warning("Connection reset received while reading Dynamips response: {}".format(e))
continue
if not chunk:
raise DynamipsError("No data returned from {host}:{port}, Dynamips process running: {run}"
.format(host=self._host, port=self._port, run=self.is_running()))
buf += chunk.decode("utf-8", errors="ignore")
except OSError as e:
raise DynamipsError("Could not read response for '{command}' from {host}:{port}: {error}, process running: {run}"
.format(command=command.strip(), host=self._host, port=self._port, error=e, run=self.is_running()))
# If the buffer doesn't end in '\n' then we can't be done
try:
if buf[-1] != '\n':
continue
except IndexError:
raise DynamipsError("Could not communicate with {host}:{port}, Dynamips process running: {run}"
.format(host=self._host, port=self._port, run=self.is_running()))
data += buf.split('\r\n')
if data[-1] == '':
data.pop()
buf = ''
# Does it contain an error code?
if self.error_re.search(data[-1]):
raise DynamipsError(data[-1][4:])
# Or does the last line begin with '100-'? Then we are done!
if data[-1][:4] == '100-':
data[-1] = data[-1][4:]
if data[-1] == 'OK':
data.pop()
break
# Remove success responses codes
for index in range(len(data)):
if self.success_re.search(data[index]):
data[index] = data[index][4:]
log.debug("returned result {}".format(data))
return data
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .dynamips_error import DynamipsError
from .nodes.c1700 import C1700
from .nodes.c2600 import C2600
from .nodes.c2691 import C2691
from .nodes.c3600 import C3600
from .nodes.c3725 import C3725
from .nodes.c3745 import C3745
from .nodes.c7200 import C7200
import logging
log = logging.getLogger(__name__)
PLATFORMS = {'c1700': C1700,
'c2600': C2600,
'c2691': C2691,
'c3725': C3725,
'c3745': C3745,
'c3600': C3600,
'c7200': C7200}
class DynamipsVM:
"""
Factory to create an Router object based on the correct platform.
"""
def __new__(cls, name, vm_id, project, manager, dynamips_id, platform, **kwargs):
if platform not in PLATFORMS:
raise DynamipsError("Unknown router platform: {}".format(platform))
return PLATFORMS[platform](name, vm_id, project, manager, dynamips_id, **kwargs)
@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Represents a Dynamips hypervisor and starts/stops the associated Dynamips process.
"""
import os
import subprocess
import tempfile
import asyncio
from gns3server.utils.asyncio import wait_for_process_termination
from .dynamips_hypervisor import DynamipsHypervisor
from .dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class Hypervisor(DynamipsHypervisor):
"""
Hypervisor.
:param path: path to Dynamips executable
:param working_dir: working directory
:param host: host/address for this hypervisor
:param port: port for this hypervisor
:param console_host: host/address for console connections
"""
_instance_count = 1
def __init__(self, path, working_dir, host, port, console_host):
super().__init__(working_dir, host, port)
# create an unique ID
self._id = Hypervisor._instance_count
Hypervisor._instance_count += 1
self._console_host = console_host
self._path = path
self._command = []
self._process = None
self._stdout_file = ""
self._started = False
@property
def id(self):
"""
Returns the unique ID for this hypervisor.
:returns: id (integer)
"""
return self._id
@property
def process(self):
"""
Returns the subprocess of the Hypervisor
:returns: subprocess
"""
return self._process
@property
def started(self):
"""
Returns either this hypervisor has been started or not.
:returns: boolean
"""
return self._started
@property
def path(self):
"""
Returns the path to the Dynamips executable.
:returns: path to Dynamips
"""
return self._path
@path.setter
def path(self, path):
"""
Sets the path to the Dynamips executable.
:param path: path to Dynamips
"""
self._path = path
@asyncio.coroutine
def start(self):
"""
Starts the Dynamips hypervisor process.
"""
self._command = self._build_command()
try:
log.info("Starting Dynamips: {}".format(self._command))
with tempfile.NamedTemporaryFile(delete=False) as fd:
self._stdout_file = fd.name
log.info("Dynamips process logging to {}".format(fd.name))
self._process = yield from asyncio.create_subprocess_exec(*self._command,
stdout=fd,
stderr=subprocess.STDOUT,
cwd=self._working_dir)
log.info("Dynamips process started PID={}".format(self._process.pid))
self._started = True
except (OSError, subprocess.SubprocessError) as e:
log.error("Could not start Dynamips: {}".format(e))
raise DynamipsError("Could not start Dynamips: {}".format(e))
@asyncio.coroutine
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)
# give some time for the hypervisor to properly stop.
# time to delete UNIX NIOs for instance.
yield from asyncio.sleep(0.01)
try:
yield from wait_for_process_termination(self._process, timeout=3)
except asyncio.TimeoutError:
if self._process.returncode is None:
log.warn("Dynamips process {} is still running... killing it".format(self._process.pid))
try:
self._process.kill()
except OSError as e:
log.error("Cannot stop the Dynamips process: {}".format(e))
if self._process.returncode is None:
log.warn('Dynamips hypervisor with PID={} is still running'.format(self._process.pid))
if self._stdout_file and os.access(self._stdout_file, os.W_OK):
try:
os.remove(self._stdout_file)
except OSError as e:
log.warning("could not delete temporary Dynamips log file: {}".format(e))
self._started = False
def read_stdout(self):
"""
Reads the standard output of the Dynamips process.
Only use when the process has been stopped or has crashed.
"""
output = ""
if self._stdout_file and os.access(self._stdout_file, os.R_OK):
try:
with open(self._stdout_file, "rb") as file:
output = file.read().decode("utf-8", errors="replace")
except OSError as e:
log.warn("could not read {}: {}".format(self._stdout_file, e))
return output
def is_running(self):
"""
Checks if the process is running
:returns: True or False
"""
if self._process and self._process.returncode is None:
return True
return False
def _build_command(self):
"""
Command to start the Dynamips hypervisor process.
(to be passed to subprocess.Popen())
"""
command = [self._path]
command.extend(["-N1"]) # use instance IDs for filenames
command.extend(["-l", "dynamips_i{}_log.txt".format(self._id)]) # log file
# Dynamips cannot listen for hypervisor commands and for console connections on
# 2 different IP addresses.
# See https://github.com/GNS3/dynamips/issues/62
if self._console_host != "0.0.0.0" and self._console_host != "::":
command.extend(["-H", "{}:{}".format(self._host, self._port)])
else:
command.extend(["-H", str(self._port)])
return command
Whitespace-only changes.
+256
View File
@@ -0,0 +1,256 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Base interface for Dynamips Network Input/Output (NIO) module ("nio").
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L451
"""
import asyncio
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class NIO:
"""
Base NIO class
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, hypervisor):
self._hypervisor = hypervisor
self._name = name
self._bandwidth = None # no bandwidth constraint by default
self._input_filter = None # no input filter applied by default
self._output_filter = None # no output filter applied by default
self._input_filter_options = None # no input filter options by default
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):
"""
Returns all NIOs.
:returns: NIO list
"""
nio_list = yield from self._hypervisor.send("nio list")
return nio_list
@asyncio.coroutine
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))
log.info("NIO {name} has been deleted".format(name=self._name))
@asyncio.coroutine
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))
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):
"""
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))
@asyncio.coroutine
def bind_filter(self, direction, filter_name):
"""
Adds a packet filter to this NIO.
Filter "freq_drop" drops packets.
Filter "capture" captures packets.
:param direction: "in", "out" or "both"
:param filter_name: name of the filter to apply
"""
if direction not in self._dynamips_direction:
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,
direction=dynamips_direction,
filter=filter_name))
if direction == "in":
self._input_filter = filter_name
elif direction == "out":
self._output_filter = filter_name
elif direction == "both":
self._input_filter = filter_name
self._output_filter = filter_name
@asyncio.coroutine
def unbind_filter(self, direction):
"""
Removes packet filter for this NIO.
:param direction: "in", "out" or "both"
"""
if direction not in self._dynamips_direction:
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,
direction=dynamips_direction))
if direction == "in":
self._input_filter = None
elif direction == "out":
self._output_filter = None
elif direction == "both":
self._input_filter = None
self._output_filter = None
@asyncio.coroutine
def setup_filter(self, direction, options):
"""
Setups a packet filter bound with this NIO.
Filter "freq_drop" has 1 argument "<frequency>". It will drop
everything with a -1 frequency, drop every Nth packet with a
positive frequency, or drop nothing.
Filter "capture" has 2 arguments "<link_type_name> <output_file>".
It will capture packets to the target output file. The link type
name is a case-insensitive DLT_ name from the PCAP library
constants with the DLT_ part removed.See http://www.tcpdump.org/linktypes.html
for a list of all available DLT values.
:param direction: "in", "out" or "both"
:param options: options for the packet filter (string)
"""
if direction not in self._dynamips_direction:
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,
direction=dynamips_direction,
options=options))
if direction == "in":
self._input_filter_options = options
elif direction == "out":
self._output_filter_options = options
elif direction == "both":
self._input_filter_options = options
self._output_filter_options = options
@property
def input_filter(self):
"""
Returns the input packet filter for this NIO.
:returns: tuple (filter name, filter options)
"""
return self._input_filter, self._input_filter_options
@property
def output_filter(self):
"""
Returns the output packet filter for this NIO.
:returns: tuple (filter name, filter options)
"""
return self._output_filter, self._output_filter_options
@asyncio.coroutine
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))
return stats[0]
@asyncio.coroutine
def reset_stats(self):
"""
Resets statistics for this NIO.
"""
yield from self._hypervisor.send("nio reset_stats {}".format(self._name))
@property
def bandwidth(self):
"""
Returns the bandwidth constraint for this NIO.
:returns: bandwidth integer value (in Kb/s)
"""
return self._bandwidth
@asyncio.coroutine
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))
self._bandwidth = bandwidth
def __str__(self):
"""
NIO string representation.
:returns: NIO name
"""
return self._name
@property
def name(self):
"""
Returns the NIO name.
:returns: NIO name
"""
return self._name
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for generic Ethernet NIOs (PCAP library).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOGenericEthernet(NIO):
"""
Dynamips generic Ethernet NIO.
:param hypervisor: Dynamips hypervisor instance
:param ethernet_device: Ethernet device name (e.g. eth0)
"""
def __init__(self, hypervisor, ethernet_device):
# create an unique name
name = 'generic_ethernet-{}'.format(uuid.uuid4())
self._ethernet_device = ethernet_device
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from 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,
device=self._ethernet_device))
@property
def ethernet_device(self):
"""
Returns the Ethernet device used by this NIO.
:returns: the Ethernet device name
"""
return self._ethernet_device
def __json__(self):
return {"type": "nio_generic_ethernet",
"ethernet_device": self._ethernet_device}
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Linux Ethernet NIOs (Linux only).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOLinuxEthernet(NIO):
"""
Dynamips Linux Ethernet NIO.
:param hypervisor: Dynamips hypervisor instance
:param ethernet_device: Ethernet device name (e.g. eth0)
"""
def __init__(self, hypervisor, ethernet_device):
# create an unique name
name = 'linux_ethernet-{}'.format(uuid.uuid4())
self._ethernet_device = ethernet_device
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from 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,
device=self._ethernet_device))
@property
def ethernet_device(self):
"""
Returns the Ethernet device used by this NIO.
:returns: the Ethernet device name
"""
return self._ethernet_device
def __json__(self):
return {"type": "nio_linux_ethernet",
"ethernet_device": self._ethernet_device}
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for dummy NIOs (mostly for tests).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIONull(NIO):
"""
Dynamips NULL NIO.
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, hypervisor):
# create an unique name
name = 'null-{}'.format(uuid.uuid4())
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from self._hypervisor.send("nio create_null {}".format(self._name))
log.info("NIO NULL {name} created.".format(name=self._name))
def __json__(self):
return {"type": "nio_null"}
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for TAP NIOs (UNIX based OSes only).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOTAP(NIO):
"""
Dynamips TAP NIO.
:param hypervisor: Dynamips hypervisor instance
:param tap_device: TAP device name (e.g. tap0)
"""
def __init__(self, hypervisor, tap_device):
# create an unique name
name = 'tap-{}'.format(uuid.uuid4())
self._tap_device = tap_device
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from 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
def tap_device(self):
"""
Returns the TAP device used by this NIO.
:returns: the TAP device name
"""
return self._tap_device
def __json__(self):
return {"type": "nio_tap",
"tap_device": self._tap_device}
@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for UDP NIOs.
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOUDP(NIO):
"""
Dynamips UDP NIO.
:param hypervisor: Dynamips hypervisor instance
:param lport: local port number
:param rhost: remote address/host
:param rport: remote port number
"""
def __init__(self, hypervisor, lport, rhost, rport):
# create an unique name
name = 'udp-{}'.format(uuid.uuid4())
self._lport = lport
self._rhost = rhost
self._rport = rport
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name,
lport=self._lport,
rhost=self._rhost,
rport=self._rport))
log.info("NIO UDP {name} created with lport={lport}, rhost={rhost}, rport={rport}".format(name=self._name,
lport=self._lport,
rhost=self._rhost,
rport=self._rport))
@property
def lport(self):
"""
Returns the local port
:returns: local port number
"""
return self._lport
@property
def rhost(self):
"""
Returns the remote host
:returns: remote address/host
"""
return self._rhost
@property
def rport(self):
"""
Returns the remote port
:returns: remote port number
"""
return self._rport
def __json__(self):
return {"type": "nio_udp",
"lport": self._lport,
"rport": self._rport,
"rhost": self._rhost}
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for UNIX NIOs (Unix based OSes only).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOUNIX(NIO):
"""
Dynamips UNIX NIO.
:param hypervisor: Dynamips hypervisor instance
:param local_file: local UNIX socket filename
:param remote_file: remote UNIX socket filename
"""
def __init__(self, hypervisor, local_file, remote_file):
# create an unique name
name = 'unix-{}'.format(uuid.uuid4())
self._local_file = local_file
self._remote_file = remote_file
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name,
local=self._local_file,
remote=self._remote_file))
log.info("NIO UNIX {name} created with local file {local} and remote file {remote}".format(name=self._name,
local=self._local_file,
remote=self._remote_file))
@property
def local_file(self):
"""
Returns the local UNIX socket.
:returns: local UNIX socket filename
"""
return self._local_file
@property
def remote_file(self):
"""
Returns the remote UNIX socket.
:returns: remote UNIX socket filename
"""
return self._remote_file
def __json__(self):
return {"type": "nio_unix",
"local_file": self._local_file,
"remote_file": self._remote_file}
@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only).
"""
import asyncio
import uuid
from .nio import NIO
import logging
log = logging.getLogger(__name__)
class NIOVDE(NIO):
"""
Dynamips VDE NIO.
:param hypervisor: Dynamips hypervisor instance
:param control_file: VDE control filename
:param local_file: VDE local filename
"""
def __init__(self, hypervisor, control_file, local_file):
# create an unique name
name = 'vde-{}'.format(uuid.uuid4())
self._control_file = control_file
self._local_file = local_file
super().__init__(name, hypervisor)
@asyncio.coroutine
def create(self):
yield from self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name,
control=self._control_file,
local=self._local_file))
log.info("NIO VDE {name} created with control={control}, local={local}".format(name=self._name,
control=self._control_file,
local=self._local_file))
@property
def control_file(self):
"""
Returns the VDE control file.
:returns: VDE control filename
"""
return self._control_file
@property
def local_file(self):
"""
Returns the VDE local file.
:returns: VDE local filename
"""
return self._local_file
def __json__(self):
return {"type": "nio_vde",
"local_file": self._local_file,
"control_file": self._control_file}
Whitespace-only changes.
@@ -0,0 +1,423 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual ATM switch module ("atmsw").
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593
"""
import asyncio
import re
from .device import Device
from ..nios.nio_udp import NIOUDP
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class ATMSwitch(Device):
"""
Dynamips ATM switch.
:param name: name for this switch
:param device_id: Device instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, device_id, project, manager, hypervisor=None):
super().__init__(name, device_id, project, manager, hypervisor)
self._nios = {}
self._mappings = {}
def __json__(self):
mappings = {}
for source, destination in self._mappings.items():
mappings[str(source)] = str(destination)
return {"name": self.name,
"device_id": self.id,
"project_id": self.project.id,
"mappings": mappings}
@asyncio.coroutine
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)
yield from 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):
"""
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))
log.info('ATM switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name
@property
def nios(self):
"""
Returns all the NIOs member of this ATM switch.
:returns: nio list
"""
return self._nios
@property
def mappings(self):
"""
Returns port mappings
:returns: mappings list
"""
return self._mappings
@asyncio.coroutine
def delete(self):
"""
Deletes this ATM switch.
"""
for nio in self._nios.values():
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
try:
yield from 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()
def has_port(self, port):
"""
Checks if a port exists on this ATM switch.
:returns: boolean
"""
if port in self._nios:
return True
return False
def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on ATM switch.
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
if port_number in self._nios:
raise DynamipsError("Port {} isn't free".format(port_number))
log.info('ATM switch "{name}" [id={id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
self._nios[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this ATM switch.
:param port_number: allocated port number
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
# remove VCs mapped with the port
for source, destination in self._mappings.copy().items():
if len(source) == 3 and len(destination) == 3:
# remove the virtual channels mapped with this port/nio
source_port, source_vpi, source_vci = source
destination_port, destination_vpi, destination_vci = destination
if port_number == source_port:
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)
else:
# remove the virtual paths mapped with this port/nio
source_port, source_vpi = source
destination_port, destination_vpi = destination
if port_number == source_port:
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)
nio = self._nios[port_number]
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
log.info('ATM switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
del self._nios[port_number]
return nio
@asyncio.coroutine
def set_mappings(self, mappings):
"""
Applies VC mappings
:param mappings: mappings (dict)
"""
pvc_entry = re.compile(r"""^([0-9]*):([0-9]*):([0-9]*)$""")
for source, destination in mappings.items():
if not isinstance(source, str) or not isinstance(destination, str):
raise DynamipsError("Invalid ATM mappings")
match_source_pvc = pvc_entry.search(source)
match_destination_pvc = pvc_entry.search(destination)
if match_source_pvc and match_destination_pvc:
# add the virtual channels
source_port, source_vpi, source_vci = map(int, match_source_pvc.group(1, 2, 3))
destination_port, destination_vpi, destination_vci = map(int, match_destination_pvc.group(1, 2, 3))
if self.has_port(destination_port):
if (source_port, source_vpi, source_vci) not in self.mappings and \
(destination_port, destination_vpi, destination_vci) not in self.mappings:
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)
else:
# add the virtual paths
source_port, source_vpi = map(int, source.split(':'))
destination_port, destination_vpi = map(int, destination.split(':'))
if self.has_port(destination_port):
if (source_port, source_vpi) not in self.mappings and (destination_port, destination_vpi) not in self.mappings:
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)
@asyncio.coroutine
def map_vp(self, port1, vpi1, port2, vpi2):
"""
Creates a new Virtual Path connection.
:param port1: input port
:param vpi1: input vpi
:param port2: output port
:param vpi2: output vpi
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_vpi=vpi1,
output_nio=nio2,
output_vpi=vpi2))
log.info('ATM switch "{name}" [{id}]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} created'.format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
port2=port2,
vpi2=vpi2))
self._mappings[(port1, vpi1)] = (port2, vpi2)
@asyncio.coroutine
def unmap_vp(self, port1, vpi1, port2, vpi2):
"""
Deletes a new Virtual Path connection.
:param port1: input port
:param vpi1: input vpi
:param port2: output port
:param vpi2: output vpi
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_vpi=vpi1,
output_nio=nio2,
output_vpi=vpi2))
log.info('ATM switch "{name}" [{id}]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} deleted'.format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
port2=port2,
vpi2=vpi2))
del self._mappings[(port1, vpi1)]
@asyncio.coroutine
def map_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
"""
Creates a new Virtual Channel connection (unidirectional).
:param port1: input port
:param vpi1: input vpi
:param vci1: input vci
:param port2: output port
:param vpi2: output vpi
:param vci2: output vci
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_vpi=vpi1,
input_vci=vci1,
output_nio=nio2,
output_vpi=vpi2,
output_vci=vci2))
log.info('ATM switch "{name}" [{id}]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} created'.format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
vci1=vci1,
port2=port2,
vpi2=vpi2,
vci2=vci2))
self._mappings[(port1, vpi1, vci1)] = (port2, vpi2, vci2)
@asyncio.coroutine
def unmap_pvc(self, port1, vpi1, vci1, port2, vpi2, vci2):
"""
Deletes a new Virtual Channel connection (unidirectional).
:param port1: input port
:param vpi1: input vpi
:param vci1: input vci
:param port2: output port
:param vpi2: output vpi
:param vci2: output vci
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_vpi=vpi1,
input_vci=vci1,
output_nio=nio2,
output_vpi=vpi2,
output_vci=vci2))
log.info('ATM switch "{name}" [{id}]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} deleted'.format(name=self._name,
id=self._id,
port1=port1,
vpi1=vpi1,
vci1=vci1,
port2=port2,
vpi2=vpi2,
vci2=vci2))
del self._mappings[(port1, vpi1, vci1)]
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_ATM_RFC1483"):
"""
Starts a packet capture.
:param port_number: allocated port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_ATM_RFC1483
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
data_link_type = data_link_type.lower()
if data_link_type.startswith("dlt_"):
data_link_type = data_link_type[4:]
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))
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):
"""
Stops a packet capture.
:param port_number: allocated port number
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
yield from 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))
@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips NIO bridge module ("nio_bridge").
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L538
"""
import asyncio
from .device import Device
class Bridge(Device):
"""
Dynamips bridge.
:param name: name for this bridge
:param node_id: Node instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, node_id, project, manager, hypervisor=None):
super().__init__(name, node_id, project, manager, hypervisor)
self._nios = []
@asyncio.coroutine
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)
yield from self._hypervisor.send('nio_bridge create "{}"'.format(self._name))
self._hypervisor.devices.append(self)
@asyncio.coroutine
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,
new_name=new_name))
self._name = new_name
@property
def nios(self):
"""
Returns all the NIOs member of this bridge.
:returns: nio list
"""
return self._nios
@asyncio.coroutine
def delete(self):
"""
Deletes this bridge.
"""
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))
@asyncio.coroutine
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))
self._nios.append(nio)
@asyncio.coroutine
def remove_nio(self, nio):
"""
Removes the specified NIO as member of this bridge.
:param nio: NIO instance to remove
"""
yield from self._hypervisor.send('nio_bridge remove_nio "{name}" {nio}'.format(name=self._name, nio=nio))
self._nios.remove(nio)
@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 1700 instances module ("c1700")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L428
"""
import asyncio
from .router import Router
from ..adapters.c1700_mb_1fe import C1700_MB_1FE
from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1
import logging
log = logging.getLogger(__name__)
class C1700(Router):
"""
Dynamips c1700 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
:param chassis: chassis for this router:
1720, 1721, 1750, 1751 or 1760 (default = 1720).
1710 is not supported.
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="1720"):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c1700")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 64
self._nvram = 32
self._disk0 = 0
self._disk1 = 0
self._chassis = chassis
self._iomem = 15 # percentage
self._clock_divisor = 8
self._sparsemem = False # never activate sparsemem for c1700 (unstable)
def __json__(self):
c1700_router_info = {"iomem": self._iomem,
"chassis": self._chassis,
"sparsemem": self._sparsemem}
router_info = Router.__json__(self)
router_info.update(c1700_router_info)
return router_info
@asyncio.coroutine
def create(self):
yield from Router.create(self)
if self._chassis != "1720":
yield from self.set_chassis(self._chassis)
self._setup_chassis()
def _setup_chassis(self):
"""
Sets up the router with the corresponding chassis
(create slots and insert default adapters).
"""
# With 1751 and 1760, WICs in WIC slot 1 show up as in slot 1, not 0
# e.g. s1/0 not s0/2
if self._chassis in ['1751', '1760']:
self._create_slots(2)
self._slots[1] = C1700_MB_WIC1()
else:
self._create_slots(1)
self._slots[0] = C1700_MB_1FE()
@property
def chassis(self):
"""
Returns the chassis.
:returns: chassis string
"""
return self._chassis
@asyncio.coroutine
def set_chassis(self, chassis):
"""
Sets the chassis.
:param: chassis string:
1720, 1721, 1750, 1751 or 1760
"""
yield from 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,
chassis=chassis))
self._chassis = chassis
self._setup_chassis()
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 2600 instances module ("c2600")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L404
"""
import asyncio
from .router import Router
from ..adapters.c2600_mb_1e import C2600_MB_1E
from ..adapters.c2600_mb_2e import C2600_MB_2E
from ..adapters.c2600_mb_1fe import C2600_MB_1FE
from ..adapters.c2600_mb_2fe import C2600_MB_2FE
import logging
log = logging.getLogger(__name__)
class C2600(Router):
"""
Dynamips c2600 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
:param chassis: chassis for this router:
2610, 2611, 2620, 2621, 2610XM, 2611XM
2620XM, 2621XM, 2650XM or 2651XM (default = 2610).
"""
# adapters to insert by default corresponding the
# chosen chassis.
integrated_adapters = {"2610": C2600_MB_1E,
"2611": C2600_MB_2E,
"2620": C2600_MB_1FE,
"2621": C2600_MB_2FE,
"2610XM": C2600_MB_1FE,
"2611XM": C2600_MB_2FE,
"2620XM": C2600_MB_1FE,
"2621XM": C2600_MB_2FE,
"2650XM": C2600_MB_1FE,
"2651XM": C2600_MB_2FE}
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="2610"):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c2600")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 64
self._nvram = 128
self._disk0 = 0
self._disk1 = 0
self._chassis = chassis
self._iomem = 15 # percentage
self._clock_divisor = 8
self._sparsemem = False # never activate sparsemem for c2600 (unstable)
def __json__(self):
c2600_router_info = {"iomem": self._iomem,
"chassis": self._chassis,
"sparsemem": self._sparsemem}
router_info = Router.__json__(self)
router_info.update(c2600_router_info)
return router_info
@asyncio.coroutine
def create(self):
yield from Router.create(self)
if self._chassis != "2610":
yield from self.set_chassis(self._chassis)
self._setup_chassis()
def _setup_chassis(self):
"""
Sets up the router with the corresponding chassis
(create slots and insert default adapters).
"""
self._create_slots(2)
self._slots[0] = self.integrated_adapters[self._chassis]()
@property
def chassis(self):
"""
Returns the chassis.
:returns: chassis string
"""
return self._chassis
@asyncio.coroutine
def set_chassis(self, chassis):
"""
Sets the chassis.
:param: chassis string:
2610, 2611, 2620, 2621, 2610XM, 2611XM
2620XM, 2621XM, 2650XM or 2651XM
"""
yield from 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,
chassis=chassis))
self._chassis = chassis
self._setup_chassis()
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 2691 instances module ("c2691")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L387
"""
import asyncio
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class C2691(Router):
"""
Dynamips c2691 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c2691")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128
self._nvram = 112
self._disk0 = 16
self._disk1 = 0
self._iomem = 5 # percentage
self._clock_divisor = 8
self._create_slots(2)
self._slots[0] = GT96100_FE()
if chassis is not None:
raise DynamipsError("c2691 routers do not have chassis")
def __json__(self):
c2691_router_info = {"iomem": self._iomem}
router_info = Router.__json__(self)
router_info.update(c2691_router_info)
return router_info
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 3600 instances module ("c3600")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L366
"""
import asyncio
from .router import Router
from ..adapters.leopard_2fe import Leopard_2FE
import logging
log = logging.getLogger(__name__)
class C3600(Router):
"""
Dynamips c3600 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
:param chassis: chassis for this router:
3620, 3640 or 3660 (default = 3640).
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis="3640"):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3600")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128
self._nvram = 128
self._disk0 = 0
self._disk1 = 0
self._iomem = 5 # percentage
self._chassis = chassis
self._clock_divisor = 4
def __json__(self):
c3600_router_info = {"iomem": self._iomem,
"chassis": self._chassis}
router_info = Router.__json__(self)
router_info.update(c3600_router_info)
return router_info
@asyncio.coroutine
def create(self):
yield from Router.create(self)
if self._chassis != "3640":
yield from self.set_chassis(self._chassis)
self._setup_chassis()
def _setup_chassis(self):
"""
Sets up the router with the corresponding chassis
(create slots and insert default adapters).
"""
if self._chassis == "3620":
self._create_slots(2)
elif self._chassis == "3640":
self._create_slots(4)
elif self._chassis == "3660":
self._create_slots(7)
self._slots[0] = Leopard_2FE()
@property
def chassis(self):
"""
Returns the chassis.
:returns: chassis string
"""
return self._chassis
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: chassis set to {chassis}'.format(name=self._name,
id=self._id,
chassis=chassis))
self._chassis = chassis
self._setup_chassis()
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 3725 instances module ("c3725")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L346
"""
import asyncio
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class C3725(Router):
"""
Dynamips c3725 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3725")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128
self._nvram = 112
self._disk0 = 16
self._disk1 = 0
self._iomem = 5 # percentage
self._clock_divisor = 8
self._create_slots(3)
self._slots[0] = GT96100_FE()
if chassis is not None:
raise DynamipsError("c3725 routers do not have chassis")
def __json__(self):
c3725_router_info = {"iomem": self._iomem}
router_info = Router.__json__(self)
router_info.update(c3725_router_info)
return router_info
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 3745 instances module ("c3745")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L326
"""
import asyncio
from .router import Router
from ..adapters.gt96100_fe import GT96100_FE
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class C3745(Router):
"""
Dynamips c3745 router.
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, chassis=None):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c3745")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 128
self._nvram = 304
self._disk0 = 16
self._disk1 = 0
self._iomem = 5 # percentage
self._clock_divisor = 8
self._create_slots(5)
self._slots[0] = GT96100_FE()
if chassis is not None:
raise DynamipsError("c3745 routers do not have chassis")
def __json__(self):
c3745_router_info = {"iomem": self._iomem}
router_info = Router.__json__(self)
router_info.update(c3745_router_info)
return router_info
@property
def iomem(self):
"""
Returns I/O memory size for this router.
:returns: I/O memory size (integer)
"""
return self._iomem
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: I/O memory updated from {old_iomem}% to {new_iomem}%'.format(name=self._name,
id=self._id,
old_iomem=self._iomem,
new_iomem=iomem))
self._iomem = iomem
@@ -0,0 +1,244 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Cisco 7200 instances module ("c7200")
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294
"""
import asyncio
from .router import Router
from ..adapters.c7200_io_fe import C7200_IO_FE
from ..adapters.c7200_io_ge_e import C7200_IO_GE_E
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class C7200(Router):
"""
Dynamips c7200 router (model is 7206).
:param name: The name of this router
:param vm_id: Router instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param dynamips_id: ID to use with Dynamips
:param console: console port
:param aux: auxiliary console port
:param npe: Default NPE
"""
def __init__(self, name, vm_id, project, manager, dynamips_id, console=None, aux=None, npe="npe-400", chassis=None):
super().__init__(name, vm_id, project, manager, dynamips_id, console, aux, platform="c7200")
# Set default values for this platform (must be the same as Dynamips)
self._ram = 256
self._nvram = 128
self._disk0 = 64
self._disk1 = 0
self._npe = npe
self._midplane = "vxr"
self._clock_divisor = 4
self._npe = npe
# 4 sensors with a default temperature of 22C:
# sensor 1 = I/0 controller inlet
# sensor 2 = I/0 controller outlet
# sensor 3 = NPE inlet
# sensor 4 = NPE outlet
self._sensors = [22, 22, 22, 22]
# 2 power supplies powered on
self._power_supplies = [1, 1]
self._create_slots(7)
if chassis is not None:
raise DynamipsError("c7200 routers do not have chassis")
def __json__(self):
c7200_router_info = {"npe": self._npe,
"midplane": self._midplane,
"sensors": self._sensors,
"power_supplies": self._power_supplies}
router_info = Router.__json__(self)
router_info.update(c7200_router_info)
return router_info
@asyncio.coroutine
def create(self):
yield from Router.create(self)
if self._npe != "npe-400":
yield from 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())
else:
yield from self.slot_add_binding(0, C7200_IO_FE())
@property
def npe(self):
"""
Returns the NPE model.
:returns: NPE model string (e.g. "npe-200")
"""
return self._npe
@asyncio.coroutine
def set_npe(self, npe):
"""
Sets the NPE model.
:params npe: NPE model string (e.g. "npe-200")
NPE models are npe-100, npe-150, npe-175, npe-200,
npe-225, npe-300, npe-400 and npe-g2 (PowerPC c7200 only)
"""
if (yield from 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))
log.info('Router "{name}" [{id}]: NPE updated from {old_npe} to {new_npe}'.format(name=self._name,
id=self._id,
old_npe=self._npe,
new_npe=npe))
self._npe = npe
@property
def midplane(self):
"""
Returns the midplane model.
:returns: midplane model string (e.g. "vxr" or "std")
"""
return self._midplane
@asyncio.coroutine
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))
log.info('Router "{name}" [{id}]: midplane updated from {old_midplane} to {new_midplane}'.format(name=self._name,
id=self._id,
old_midplane=self._midplane,
new_midplane=midplane))
self._midplane = midplane
@property
def sensors(self):
"""
Returns the 4 sensors with temperature in degree Celcius.
:returns: list of 4 sensor temperatures
"""
return self._sensors
@asyncio.coroutine
def set_sensors(self, sensors):
"""
Sets the 4 sensors with temperature in degree Celcius.
:param sensors: list of 4 sensor temperatures corresponding to
sensor 1 = I/0 controller inlet
sensor 2 = I/0 controller outlet
sensor 3 = NPE inlet
sensor 4 = NPE outlet
Example: [22, 22, 22, 22]
"""
sensor_id = 0
for sensor in sensors:
yield from self._hypervisor.send('c7200 set_temp_sensor "{name}" {sensor_id} {temp}'.format(name=self._name,
sensor_id=sensor_id,
temp=sensor))
log.info('Router "{name}" [{id}]: sensor {sensor_id} temperature updated from {old_temp}C to {new_temp}C'.format(name=self._name,
id=self._id,
sensor_id=sensor_id,
old_temp=self._sensors[sensor_id],
new_temp=sensors[sensor_id]))
sensor_id += 1
self._sensors = sensors
@property
def power_supplies(self):
"""
Returns the 2 power supplies with 0 = off, 1 = on.
:returns: list of 2 power supplies.
"""
return self._power_supplies
@asyncio.coroutine
def set_power_supplies(self, power_supplies):
"""
Sets the 2 power supplies with 0 = off, 1 = on.
:param power_supplies: list of 2 power supplies.
Example: [1, 0] = first power supply is on, second is off.
"""
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,
power_supply_id=power_supply_id,
powered_on=power_supply))
log.info('Router "{name}" [{id}]: power supply {power_supply_id} state updated to {powered_on}'.format(name=self._name,
id=self._id,
power_supply_id=power_supply_id,
powered_on=power_supply))
power_supply_id += 1
self._power_supplies = power_supplies
@asyncio.coroutine
def start(self):
"""
Starts this router.
At least the IOS image must be set before starting it.
"""
# 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)
if self._sensors != [22, 22, 22, 22]:
yield from self.set_sensors(self._sensors)
if self._power_supplies != [1, 1]:
yield from self.set_power_supplies(self._power_supplies)
@@ -0,0 +1,104 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class Device:
"""
Base device for switches and hubs
:param name: name for this device
:param device_id: Device instance identifier
:param project: Project instance
:param manager: Parent manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, device_id, project, manager, hypervisor=None):
self._name = name
self._id = device_id
self._project = project
self._manager = manager
self._hypervisor = hypervisor
@property
def hypervisor(self):
"""
Returns the current hypervisor.
:returns: hypervisor instance
"""
return self._hypervisor
@property
def project(self):
"""
Returns the device current project.
:returns: Project instance.
"""
return self._project
@property
def name(self):
"""
Returns the name for this device.
:returns: name
"""
return self._name
@name.setter
def name(self, new_name):
"""
Sets the name of this VM.
:param new_name: name
"""
self._name = new_name
@property
def id(self):
"""
Returns the ID for this device.
:returns: device identifier (string)
"""
return self._id
@property
def manager(self):
"""
Returns the manager for this device.
:returns: instance of manager
"""
return self._manager
def create(self):
"""
Creates the device.
"""
raise NotImplementedError
@@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Hub object that uses the Bridge interface to create a hub with ports.
"""
import asyncio
from .bridge import Bridge
from ..nios.nio_udp import NIOUDP
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class EthernetHub(Bridge):
"""
Dynamips Ethernet hub (based on Bridge)
:param name: name for this hub
:param device_id: Device instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, device_id, project, manager, hypervisor=None):
super().__init__(name, device_id, project, manager, hypervisor)
self._mappings = {}
def __json__(self):
return {"name": self.name,
"device_id": self.id,
"project_id": self.project.id}
@asyncio.coroutine
def create(self):
yield from Bridge.create(self)
log.info('Ethernet hub "{name}" [{id}] has been created'.format(name=self._name, id=self._id))
@property
def mappings(self):
"""
Returns port mappings
:returns: mappings list
"""
return self._mappings
@asyncio.coroutine
def delete(self):
"""
Deletes this hub.
"""
for nio in self._nios:
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
try:
yield from 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()
@asyncio.coroutine
def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on this hub.
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
if port_number in self._mappings:
raise DynamipsError("Port {} isn't free".format(port_number))
yield from Bridge.add_nio(self, nio)
log.info('Ethernet hub "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
self._mappings[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this hub.
:param port_number: allocated port number
:returns: the NIO that was bound to the allocated port
"""
if port_number not in self._mappings:
raise DynamipsError("Port {} is not allocated".format(port_number))
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)
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
del self._mappings[port_number]
return nio
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
:param port_number: allocated port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
"""
if port_number not in self._mappings:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._mappings[port_number]
data_link_type = data_link_type.lower()
if data_link_type.startswith("dlt_"):
data_link_type = data_link_type[4:]
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))
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):
"""
Stops a packet capture.
:param port_number: allocated port number
"""
if port_number not in self._mappings:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._mappings[port_number]
yield from 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))
@@ -0,0 +1,343 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Ethernet switch module ("ethsw").
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558
"""
import asyncio
from pkg_resources import parse_version
from .device import Device
from ..nios.nio_udp import NIOUDP
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class EthernetSwitch(Device):
"""
Dynamips Ethernet switch.
:param name: name for this switch
:param device_id: Device instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, device_id, project, manager, hypervisor=None):
super().__init__(name, device_id, project, manager, hypervisor)
self._nios = {}
self._mappings = {}
def __json__(self):
ethernet_switch_info = {"name": self.name,
"device_id": self.id,
"project_id": self.project.id}
ports = []
for port_number, settings in self._mappings.items():
ports.append({"port": port_number,
"type": settings[0],
"vlan": settings[1],
"ethertype": settings[2] if len(settings) > 2 else ""})
ethernet_switch_info["ports"] = ports
return ethernet_switch_info
@asyncio.coroutine
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)
yield from 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._hypervisor.devices.append(self)
@asyncio.coroutine
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))
log.info('Ethernet switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name
@property
def nios(self):
"""
Returns all the NIOs member of this Ethernet switch.
:returns: nio list
"""
return self._nios
@property
def mappings(self):
"""
Returns port mappings
:returns: mappings list
"""
return self._mappings
@asyncio.coroutine
def delete(self):
"""
Deletes this Ethernet switch.
"""
for nio in self._nios.values():
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
try:
yield from 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()
@asyncio.coroutine
def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on Ethernet switch.
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
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))
log.info('Ethernet switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
self._nios[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this Ethernet switch.
:param port_number: allocated port number
:returns: the NIO that was bound to the port
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
yield from 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,
nio=nio,
port=port_number))
del self._nios[port_number]
if port_number in self._mappings:
del self._mappings[port_number]
return nio
@asyncio.coroutine
def set_port_settings(self, port_number, settings):
"""
Applies port settings to a specific port.
:param port_number: port number to set the settings
:param settings: port settings
"""
if settings["type"] == "access":
yield from self.set_access_port(port_number, settings["vlan"])
elif settings["type"] == "dot1q":
yield from self.set_dot1q_port(port_number, settings["vlan"])
elif settings["type"] == "qinq":
yield from self.set_qinq_port(port_number, settings["vlan"], settings["ethertype"])
@asyncio.coroutine
def set_access_port(self, port_number, vlan_id):
"""
Sets the specified port as an ACCESS port.
:param port_number: allocated port number
:param vlan_id: VLAN number membership
"""
if port_number not in self._nios:
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,
nio=nio,
vlan_id=vlan_id))
log.info('Ethernet switch "{name}" [{id}]: port {port} set as an access port in VLAN {vlan_id}'.format(name=self._name,
id=self._id,
port=port_number,
vlan_id=vlan_id))
self._mappings[port_number] = ("access", vlan_id)
@asyncio.coroutine
def set_dot1q_port(self, port_number, native_vlan):
"""
Sets the specified port as a 802.1Q trunk port.
:param port_number: allocated port number
:param native_vlan: native VLAN for this trunk port
"""
if port_number not in self._nios:
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,
nio=nio,
native_vlan=native_vlan))
log.info('Ethernet switch "{name}" [{id}]: port {port} set as a 802.1Q port with native VLAN {vlan_id}'.format(name=self._name,
id=self._id,
port=port_number,
vlan_id=native_vlan))
self._mappings[port_number] = ("dot1q", native_vlan)
@asyncio.coroutine
def set_qinq_port(self, port_number, outer_vlan, ethertype):
"""
Sets the specified port as a trunk (QinQ) port.
:param port_number: allocated port number
:param outer_vlan: outer VLAN (transport VLAN) for this QinQ port
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
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,
nio=nio,
outer_vlan=outer_vlan,
ethertype=ethertype if ethertype != "0x8100" else ""))
log.info('Ethernet switch "{name}" [{id}]: port {port} set as a QinQ ({ethertype}) port with outer VLAN {vlan_id}'.format(name=self._name,
id=self._id,
port=port_number,
vlan_id=outer_vlan,
ethertype=ethertype))
self._mappings[port_number] = ("qinq", outer_vlan, ethertype)
@asyncio.coroutine
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))
return mac_addr_table
@asyncio.coroutine
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))
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_EN10MB"):
"""
Starts a packet capture.
:param port_number: allocated port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
if not nio:
raise DynamipsError("Port {} is not connected".format(port_number))
data_link_type = data_link_type.lower()
if data_link_type.startswith("dlt_"):
data_link_type = data_link_type[4:]
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))
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):
"""
Stops a packet capture.
:param port_number: allocated port number
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
if not nio:
raise DynamipsError("Port {} is not connected".format(port_number))
yield from 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))
@@ -0,0 +1,320 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Frame-Relay switch module.
http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L642
"""
import asyncio
from .device import Device
from ..nios.nio_udp import NIOUDP
from ..dynamips_error import DynamipsError
import logging
log = logging.getLogger(__name__)
class FrameRelaySwitch(Device):
"""
Dynamips Frame Relay switch.
:param name: name for this switch
:param device_id: Device instance identifier
:param project: Project instance
:param manager: Parent VM Manager
:param hypervisor: Dynamips hypervisor instance
"""
def __init__(self, name, device_id, project, manager, hypervisor=None):
super().__init__(name, device_id, project, manager, hypervisor)
self._nios = {}
self._mappings = {}
def __json__(self):
mappings = {}
for source, destination in self._mappings.items():
mappings[str(source)] = str(destination)
return {"name": self.name,
"device_id": self.id,
"project_id": self.project.id,
"mappings": mappings}
@asyncio.coroutine
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)
yield from 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):
"""
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))
log.info('Frame Relay switch "{name}" [{id}]: renamed to "{new_name}"'.format(name=self._name,
id=self._id,
new_name=new_name))
self._name = new_name
@property
def nios(self):
"""
Returns all the NIOs member of this Frame Relay switch.
:returns: nio list
"""
return self._nios
@property
def mappings(self):
"""
Returns port mappings
:returns: mappings list
"""
return self._mappings
@asyncio.coroutine
def delete(self):
"""
Deletes this Frame Relay switch.
"""
for nio in self._nios.values():
if nio and isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
try:
yield from 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))
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()
def has_port(self, port):
"""
Checks if a port exists on this Frame Relay switch.
:returns: boolean
"""
if port in self._nios:
return True
return False
def add_nio(self, nio, port_number):
"""
Adds a NIO as new port on Frame Relay switch.
:param nio: NIO instance to add
:param port_number: port to allocate for the NIO
"""
if port_number in self._nios:
raise DynamipsError("Port {} isn't free".format(port_number))
log.info('Frame Relay switch "{name}" [{id}]: NIO {nio} bound to port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
self._nios[port_number] = nio
@asyncio.coroutine
def remove_nio(self, port_number):
"""
Removes the specified NIO as member of this Frame Relay switch.
:param port_number: allocated port number
:returns: the NIO that was bound to the allocated port
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
# remove VCs mapped with the port
for source, destination in self._mappings.copy().items():
source_port, source_dlci = source
destination_port, destination_dlci = destination
if port_number == source_port:
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)
nio = self._nios[port_number]
if isinstance(nio, NIOUDP):
self.manager.port_manager.release_udp_port(nio.lport, self._project)
log.info('Frame Relay switch "{name}" [{id}]: NIO {nio} removed from port {port}'.format(name=self._name,
id=self._id,
nio=nio,
port=port_number))
del self._nios[port_number]
return nio
@asyncio.coroutine
def set_mappings(self, mappings):
"""
Applies VC mappings
:param mappings: mappings (dict)
"""
for source, destination in mappings.items():
if not isinstance(source, str) or not isinstance(destination, str):
raise DynamipsError("Invalid Frame-Relay mappings")
source_port, source_dlci = map(int, source.split(':'))
destination_port, destination_dlci = map(int, destination.split(':'))
if self.has_port(destination_port):
if (source_port, source_dlci) not in self.mappings and (destination_port, destination_dlci) not in self.mappings:
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)
@asyncio.coroutine
def map_vc(self, port1, dlci1, port2, dlci2):
"""
Creates a new Virtual Circuit connection (unidirectional).
:param port1: input port
:param dlci1: input DLCI
:param port2: output port
:param dlci2: output DLCI
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_dlci=dlci1,
output_nio=nio2,
output_dlci=dlci2))
log.info('Frame Relay switch "{name}" [{id}]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} created'.format(name=self._name,
id=self._id,
port1=port1,
dlci1=dlci1,
port2=port2,
dlci2=dlci2))
self._mappings[(port1, dlci1)] = (port2, dlci2)
@asyncio.coroutine
def unmap_vc(self, port1, dlci1, port2, dlci2):
"""
Deletes a Virtual Circuit connection (unidirectional).
:param port1: input port
:param dlci1: input DLCI
:param port2: output port
:param dlci2: output DLCI
"""
if port1 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port1))
if port2 not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port2))
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,
input_nio=nio1,
input_dlci=dlci1,
output_nio=nio2,
output_dlci=dlci2))
log.info('Frame Relay switch "{name}" [{id}]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} deleted'.format(name=self._name,
id=self._id,
port1=port1,
dlci1=dlci1,
port2=port2,
dlci2=dlci2))
del self._mappings[(port1, dlci1)]
@asyncio.coroutine
def start_capture(self, port_number, output_file, data_link_type="DLT_FRELAY"):
"""
Starts a packet capture.
:param port_number: allocated port number
:param output_file: PCAP destination file for the capture
:param data_link_type: PCAP data link type (DLT_*), default is DLT_FRELAY
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
data_link_type = data_link_type.lower()
if data_link_type.startswith("dlt_"):
data_link_type = data_link_type[4:]
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))
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):
"""
Stops a packet capture.
:param port_number: allocated port number
"""
if port_number not in self._nios:
raise DynamipsError("Port {} is not allocated".format(port_number))
nio = self._nios[port_number]
yield from 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