diff --git a/gns3server/modules/dynamips/__init__.py b/gns3server/modules/dynamips/__init__.py index 83261edc..5ae34ad3 100644 --- a/gns3server/modules/dynamips/__init__.py +++ b/gns3server/modules/dynamips/__init__.py @@ -16,10 +16,61 @@ # along with this program. If not, see . from gns3server.modules import IModule +import gns3server.jsonrpc as jsonrpc from .hypervisor import Hypervisor from .hypervisor_manager import HypervisorManager from .dynamips_error import DynamipsError + +# nodes from .nodes.router import Router +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 +from .nodes.bridge import Bridge +from .nodes.ethernet_switch import EthernetSwitch +from .nodes.atm_switch import ATMSwitch +from .nodes.atm_bridge import ATMBridge +from .nodes.frame_relay_switch import FrameRelaySwitch +from .nodes.hub import Hub + +# 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_1t import WIC_1T +from .adapters.wic_2t import WIC_2T +from .adapters.wic_1enet import WIC_1ENET + +# NIOs +from .nios.nio_udp import NIO_UDP +from .nios.nio_udp_auto import NIO_UDP_auto +from .nios.nio_unix import NIO_UNIX +from .nios.nio_vde import NIO_VDE +from .nios.nio_tap import NIO_TAP +from .nios.nio_generic_ethernet import NIO_GenericEthernet +from .nios.nio_linux_ethernet import NIO_LinuxEthernet +from .nios.nio_fifo import NIO_FIFO +from .nios.nio_mcast import NIO_Mcast +from .nios.nio_null import NIO_Null + import logging log = logging.getLogger(__name__) @@ -29,21 +80,25 @@ class Dynamips(IModule): def __init__(self, name=None, args=(), kwargs={}): IModule.__init__(self, name=name, args=args, kwargs=kwargs) + + # start the hypervisor manager #self._hypervisor_manager = HypervisorManager("/usr/bin/dynamips", "/tmp") - @IModule.route("dynamips/echo") + @IModule.route("dynamips.echo") def echo(self, request): - print("Echo!") + if request == None: + self.send_param_error() + return log.debug("received request {}".format(request)) self.send_response(request) - @IModule.route("dynamips/create_vm") + @IModule.route("dynamips.create_vm") def create_vm(self, request): print("Create VM!") log.debug("received request {}".format(request)) self.send_response(request) - @IModule.route("dynamips/start_vm") + @IModule.route("dynamips.start_vm") def start_vm(self, request): print("Start VM!") log.debug("received request {}".format(request)) diff --git a/gns3server/stomp/__init__.py b/gns3server/modules/dynamips/adapters/__init__.py similarity index 100% rename from gns3server/stomp/__init__.py rename to gns3server/modules/dynamips/adapters/__init__.py diff --git a/gns3server/modules/dynamips/adapters/adapter.py b/gns3server/modules/dynamips/adapters/adapter.py new file mode 100644 index 00000000..4e10bf99 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/adapter.py @@ -0,0 +1,155 @@ +# -*- 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 . + + +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_id in range(0, interfaces): + self._ports[port_id] = 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_id): + """ + Checks if a port exists on this adapter. + + :returns: True is the port exists, + False otherwise. + """ + + if port_id 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] == 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 object + """ + + 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_id = base + wic_port + self._ports[port_id] = 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_id = base + wic_port + del self._ports[port_id] + self._wics[wic_slot_id] = None + + def add_nio(self, port_id, nio): + """ + Adds a NIO to a port on this adapter. + + :param port_id: port ID (integer) + :param nio: NIO object + """ + + self._ports[port_id] = nio + + def remove_nio(self, port_id): + """ + Removes a NIO from a port on this adapter. + + :param port_id: port ID (integer) + """ + + self._ports[port_id] = None + + @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 objects + """ + + return self._wics diff --git a/gns3server/modules/dynamips/adapters/c1700_mb_1fe.py b/gns3server/modules/dynamips/adapters/c1700_mb_1fe.py new file mode 100644 index 00000000..71f992e7 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c1700_mb_1fe.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C1700_MB_1FE(Adapter): + """ + Integrated 1 port FastEthernet adapter for c1700 platform. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1, wics=2) + + def __str__(self): + + return "C1700-MB-1FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c1700_mb_wic1.py b/gns3server/modules/dynamips/adapters/c1700_mb_wic1.py new file mode 100644 index 00000000..7705984c --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c1700_mb_wic1.py @@ -0,0 +1,44 @@ +# -*- 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 . + +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): + Adapter.__init__(self, interfaces=0, wics=2) + + def __str__(self): + + return "C1700_MB_WIC1" + + def removable(self): + + return False + + def interface_type(self): + + return "N/A" + + def medium(self): + + return "N/A" diff --git a/gns3server/modules/dynamips/adapters/c2600_mb_1e.py b/gns3server/modules/dynamips/adapters/c2600_mb_1e.py new file mode 100644 index 00000000..2ccb1661 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c2600_mb_1e.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C2600_MB_1E(Adapter): + """ + Integrated 1 port Ethernet adapter for the c2600 platform. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1, wics=3) + + def __str__(self): + + return "C2600-MB-1E" + + def removable(self): + + return False + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c2600_mb_1fe.py b/gns3server/modules/dynamips/adapters/c2600_mb_1fe.py new file mode 100644 index 00000000..a630c194 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c2600_mb_1fe.py @@ -0,0 +1,44 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C2600_MB_1FE(Adapter): + """ + Integrated 1 port FastEthernet adapter for the c2600 platform. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1, wics=3) + self._interfaces = 1 + + def __str__(self): + + return "C2600-MB-1FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c2600_mb_2e.py b/gns3server/modules/dynamips/adapters/c2600_mb_2e.py new file mode 100644 index 00000000..68ea2db8 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c2600_mb_2e.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C2600_MB_2E(Adapter): + """ + Integrated 2 port Ethernet adapter for the c2600 platform. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=2, wics=3) + + def __str__(self): + + return "C2600-MB-2E" + + def removable(self): + + return False + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c2600_mb_2fe.py b/gns3server/modules/dynamips/adapters/c2600_mb_2fe.py new file mode 100644 index 00000000..3a2d59be --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c2600_mb_2fe.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C2600_MB_2FE(Adapter): + """ + Integrated 2 port FastEthernet adapter for the c2600 platform. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=2, wics=3) + + def __str__(self): + + return "C2600-MB-2FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c7200_io_2fe.py b/gns3server/modules/dynamips/adapters/c7200_io_2fe.py new file mode 100644 index 00000000..0ee8674f --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c7200_io_2fe.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C7200_IO_2FE(Adapter): + """ + C7200-IO-2FE FastEthernet Input/Ouput controller. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=2) + + def __str__(self): + + return "C7200_IO_2FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c7200_io_fe.py b/gns3server/modules/dynamips/adapters/c7200_io_fe.py new file mode 100644 index 00000000..22200ded --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c7200_io_fe.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C7200_IO_FE(Adapter): + """ + C7200-IO-FE FastEthernet Input/Ouput controller. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "C7200_IO_FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/c7200_io_ge_e.py b/gns3server/modules/dynamips/adapters/c7200_io_ge_e.py new file mode 100644 index 00000000..dd732dfa --- /dev/null +++ b/gns3server/modules/dynamips/adapters/c7200_io_ge_e.py @@ -0,0 +1,43 @@ +# -*- 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 . + +from .adapter import Adapter + + +class C7200_IO_GE_E(Adapter): + """ + C7200-IO-GE-E GigabitEthernet Input/Ouput controller. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "C7200-IO-GE-E" + + def removable(self): + + return False + + def interface_type(self): + + return "GigabitEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/gt96100_fe.py b/gns3server/modules/dynamips/adapters/gt96100_fe.py new file mode 100644 index 00000000..27217e31 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/gt96100_fe.py @@ -0,0 +1,40 @@ +# -*- 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 . + +from .adapter import Adapter + + +class GT96100_FE(Adapter): + + def __init__(self): + Adapter.__init__(self, interfaces=2, wics=3) + + def __str__(self): + + return "GT96100-FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/leopard_2fe.py b/gns3server/modules/dynamips/adapters/leopard_2fe.py new file mode 100644 index 00000000..c25744eb --- /dev/null +++ b/gns3server/modules/dynamips/adapters/leopard_2fe.py @@ -0,0 +1,44 @@ +# -*- 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 . + +from .adapter import Adapter + + +class Leopard_2FE(Adapter): + """ + Integrated 2 port FastEthernet adapter for c3660 router. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=2) + self._interfaces = 2 + + def __str__(self): + + return "Leopard-2FE" + + def removable(self): + + return False + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/nm_16esw.py b/gns3server/modules/dynamips/adapters/nm_16esw.py new file mode 100644 index 00000000..ea7bccce --- /dev/null +++ b/gns3server/modules/dynamips/adapters/nm_16esw.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class NM_16ESW(Adapter): + """ + NM-16ESW FastEthernet network module. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=16) + + def __str__(self): + + return "NM-16ESW" + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/nm_1e.py b/gns3server/modules/dynamips/adapters/nm_1e.py new file mode 100644 index 00000000..7663f934 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/nm_1e.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class NM_1E(Adapter): + """ + NM-1E Ethernet network module. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "NM-1E" + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/nm_1fe_tx.py b/gns3server/modules/dynamips/adapters/nm_1fe_tx.py new file mode 100644 index 00000000..02f19248 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/nm_1fe_tx.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class NM_1FE_TX(Adapter): + """ + NM-1FE-TX FastEthernet network module. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "NM-1FE-TX" + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/nm_4e.py b/gns3server/modules/dynamips/adapters/nm_4e.py new file mode 100644 index 00000000..32219522 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/nm_4e.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class NM_4E(Adapter): + """ + NM-4E Ethernet network module. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=4) + + def __str__(self): + + return "NM-4E" + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/nm_4t.py b/gns3server/modules/dynamips/adapters/nm_4t.py new file mode 100644 index 00000000..1021dc51 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/nm_4t.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class NM_4T(Adapter): + """ + NM-4T Serial network module. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=4) + + def __str__(self): + + return "NM-4T" + + def interface_type(self): + + return "Serial" + + def medium(self): + + return "Serial" diff --git a/gns3server/modules/dynamips/adapters/pa_2fe_tx.py b/gns3server/modules/dynamips/adapters/pa_2fe_tx.py new file mode 100644 index 00000000..2706f51d --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_2fe_tx.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_2FE_TX(Adapter): + """ + PA-2FE-TX FastEthernet port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=2) + + def __str__(self): + + return "PA-2FE-TX" + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/pa_4e.py b/gns3server/modules/dynamips/adapters/pa_4e.py new file mode 100644 index 00000000..44f5437d --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_4e.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_4E(Adapter): + """ + PA-4E Ethernet port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=4) + + def __str__(self): + + return "PA-4E" + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/pa_4t.py b/gns3server/modules/dynamips/adapters/pa_4t.py new file mode 100644 index 00000000..592eb688 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_4t.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_4T(Adapter): + """ + PA-4T+ Serial port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=4) + + def __str__(self): + + return "PA-4T+" + + def interface_type(self): + + return "Serial" + + def medium(self): + + return "Serial" diff --git a/gns3server/modules/dynamips/adapters/pa_8e.py b/gns3server/modules/dynamips/adapters/pa_8e.py new file mode 100644 index 00000000..4e5b5a7f --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_8e.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_8E(Adapter): + """ + PA-8E Ethernet port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=8) + + def __str__(self): + + return "PA-8E" + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/pa_8t.py b/gns3server/modules/dynamips/adapters/pa_8t.py new file mode 100644 index 00000000..c4851154 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_8t.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_8T(Adapter): + """ + PA-8T Serial port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=8) + + def __str__(self): + + return "PA-8T" + + def interface_type(self): + + return "Serial" + + def medium(self): + + return "Serial" diff --git a/gns3server/modules/dynamips/adapters/pa_a1.py b/gns3server/modules/dynamips/adapters/pa_a1.py new file mode 100644 index 00000000..72cb455e --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_a1.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_A1(Adapter): + """ + PA-A1 ATM port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "PA-A1" + + def interface_type(self): + + return "ATM" + + def medium(self): + + return "Serial" diff --git a/gns3server/modules/dynamips/adapters/pa_fe_tx.py b/gns3server/modules/dynamips/adapters/pa_fe_tx.py new file mode 100644 index 00000000..7a22e2af --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_fe_tx.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_FE_TX(Adapter): + """ + PA-FE-TX FastEthernet port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "PA-FE-TX" + + def interface_type(self): + + return "FastEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/pa_ge.py b/gns3server/modules/dynamips/adapters/pa_ge.py new file mode 100644 index 00000000..20d5994b --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_ge.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_GE(Adapter): + """ + PA-GE GigabitEthernet port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "PA-GE" + + def interface_type(self): + + return "GigabitEthernet" + + def medium(self): + + return "Ethernet" diff --git a/gns3server/modules/dynamips/adapters/pa_pos_oc3.py b/gns3server/modules/dynamips/adapters/pa_pos_oc3.py new file mode 100644 index 00000000..064f63cf --- /dev/null +++ b/gns3server/modules/dynamips/adapters/pa_pos_oc3.py @@ -0,0 +1,39 @@ +# -*- 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 . + +from .adapter import Adapter + + +class PA_POS_OC3(Adapter): + """ + PA-POS-OC3 port adapter. + """ + + def __init__(self): + Adapter.__init__(self, interfaces=1) + + def __str__(self): + + return "PA-POS-OC3" + + def interface_type(self): + + return "POS" + + def medium(self): + + return "SONET" diff --git a/gns3server/modules/dynamips/adapters/wic_1enet.py b/gns3server/modules/dynamips/adapters/wic_1enet.py new file mode 100644 index 00000000..790cf618 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/wic_1enet.py @@ -0,0 +1,48 @@ +# -*- 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 . + + +class WIC_1ENET(object): + """ + WIC-1ENET Ethernet + """ + + def __init__(self): + + self._interfaces = 2 + + def __str__(self): + + return "WIC-1ENET" + + def interface_type(self): + + return "Ethernet" + + def medium(self): + + return "Ethernet" + + @property + def interfaces(self): + """ + Returns the number of interfaces supported by this WIC. + + :returns: number of interfaces + """ + + return self._interfaces diff --git a/gns3server/modules/dynamips/adapters/wic_1t.py b/gns3server/modules/dynamips/adapters/wic_1t.py new file mode 100644 index 00000000..c6f30c26 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/wic_1t.py @@ -0,0 +1,48 @@ +# -*- 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 . + + +class WIC_1T(object): + """ + WIC-1T Serial + """ + + def __init__(self): + + self._interfaces = 1 + + def __str__(self): + + return "WIC-1T" + + def interface_type(self): + + return "Serial" + + def medium(self): + + return "Serial" + + @property + def interfaces(self): + """ + Returns the number of interfaces supported by this WIC. + + :returns: number of interfaces + """ + + return self._interfaces diff --git a/gns3server/modules/dynamips/adapters/wic_2t.py b/gns3server/modules/dynamips/adapters/wic_2t.py new file mode 100644 index 00000000..8e73cb35 --- /dev/null +++ b/gns3server/modules/dynamips/adapters/wic_2t.py @@ -0,0 +1,48 @@ +# -*- 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 . + + +class WIC_2T(object): + """ + WIC-2T Serial + """ + + def __init__(self): + + self._interfaces = 2 + + def __str__(self): + + return "WIC-2T" + + def interface_type(self): + + return "Serial" + + def medium(self): + + return "Serial" + + @property + def interfaces(self): + """ + Returns the number of interfaces supported by this WIC. + + :returns: number of interfaces + """ + + return self._interfaces diff --git a/gns3server/stomp/utils.py b/gns3server/modules/dynamips/dynamips_error.py similarity index 55% rename from gns3server/stomp/utils.py rename to gns3server/modules/dynamips/dynamips_error.py index 8dd1b7e4..285e4a7b 100644 --- a/gns3server/stomp/utils.py +++ b/gns3server/modules/dynamips/dynamips_error.py @@ -16,28 +16,18 @@ # along with this program. If not, see . """ -Utilitary functions for STOMP implementation +Custom exceptions for Dynamips module. """ -import sys -PY2 = sys.version_info[0] == 2 +class DynamipsError(Exception): -if not PY2: - def encode(char_data): - if type(char_data) is str: - return char_data.encode() - elif type(char_data) is bytes: - return char_data - else: - raise TypeError('message should be a string or bytes') -else: - def encode(char_data): - if type(char_data) is unicode: - return char_data.encode('utf-8') - else: - return char_data + def __init__(self, message, original_exception=None): + Exception.__init__(self, message) + self._message = message + self._original_exception = original_exception -def hasbyte(byte, byte_data): - return bytes([byte]) in byte_data + def __repr__(self): + + return self._message diff --git a/gns3server/modules/dynamips/dynamips_hypervisor.py b/gns3server/modules/dynamips/dynamips_hypervisor.py new file mode 100644 index 00000000..0bedc7b7 --- /dev/null +++ b/gns3server/modules/dynamips/dynamips_hypervisor.py @@ -0,0 +1,419 @@ +# -*- 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 . + +""" +Interface for Dynamips hypervisor management module ("hypervisor") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L46 +""" + +import socket +import re +import logging +from .dynamips_error import DynamipsError + +log = logging.getLogger(__name__) + + +class DynamipsHypervisor(object): + """ + Creates a new connection to a Dynamips server (also called hypervisor) + + :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, host, port=7200, timeout=30.0): + + self._host = host + self._port = port + + self._devices = [] + self._ghosts = {} + self._jitsharing_groups = {} + self._working_dir = "" + self._baseconsole = 2000 + self._baseaux = 2100 + self._baseudp = 10000 + self._version = "N/A" + self._timeout = 30 + self._socket = None + self._uuid = None + + def connect(self): + """ + Connects to the hypervisor. + """ + + try: + self._socket = socket.create_connection((self._host, + self._port), + self._timeout) + except socket.error as e: + raise DynamipsError("Could not connect to server: {}".format(e)) + + try: + self._version = self.send("hypervisor version")[0][4:] + except IndexError: + self._version = "Unknown" + + self._uuid = self.send("hypervisor uuid") + + @property + def version(self): + """ + Returns Dynamips version. + + :returns: version string + """ + + return self._version + + def module_list(self): + """ + Returns the modules supported by this hypervisor. + + :returns: module list + """ + + return self.send("hypervisor module_list") + + def cmd_list(self, module): + """ + Returns commands recognized by the specified module. + + :param module: the module name + :returns: command list + """ + + return self.send("hypervisor cmd_list {}".format(module)) + + def close(self): + """ + Closes the connection to this hypervisor (but leave it running). + """ + + self.send("hypervisor close") + self._socket.close() + self._socket = None + + def stop(self): + """ + Stops this hypervisor (will no longer run). + """ + + self.send("hypervisor stop") + self._socket.close() + self._socket = None + + def reset(self): + """ + Resets this hypervisor (used to get an empty configuration). + """ + + self.send('hypervisor reset') + + @property + def working_dir(self): + """ + Returns current working directory + + :returns: path to the working directory + """ + + return self._working_dir + + @working_dir.setter + def working_dir(self, working_dir): + """ + Set 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 + self.send("hypervisor working_dir {}".format('"' + working_dir + '"')) + self._working_dir = working_dir + + def save_config(self, filename): + """ + Saves the configuration of all Dynamips objects into the specified file. + + :param filename: path string + """ + + # encase working_dir in quotes to protect spaces in the path + self.send("hypervisor save_config {}".format('"' + filename + '"')) + + @property + def uuid(self): + """ + Returns this hypervisor UUID. + + :Returns: uuid string + """ + + return self._uuid + + @property + def socket(self): + """ + Returns the current socket used to communicate with this hypervisor. + + :returns: socket object + """ + + assert self._socket + return self._socket + + @property + def devices(self): + """ + Returns the list of devices managed by this hypervisor instance. + + :returns: a list of device objects + """ + + return self._devices + + @devices.setter + def devices(self, devices): + """ + Set the list of devices managed by this hypervisor instance. + This method is for internal use. + + :param devices: a list of device objects + """ + + self._devices = devices + + @property + def baseconsole(self): + """ + Returns base console TCP port for this hypervisor. + + :returns: base console value (integer) + """ + + return self._baseconsole + + @baseconsole.setter + def baseconsole(self, baseconsole): + """ + Set the base console TCP port for this hypervisor. + + :param baseconsole: base console value (integer) + """ + + self._baseconsole = baseconsole + + @property + def baseaux(self): + """ + Returns base auxiliary port for this hypervisor. + + :returns: base auxiliary port value (integer) + """ + + return self._baseaux + + @baseaux.setter + def baseaux(self, baseaux): + """ + Set the base auxiliary TCP port for this hypervisor. + + :param baseaux: base auxiliary port value (integer) + """ + + self._baseaux = baseaux + + @property + def baseudp(self): + """ + Returns the next available UDP port for UDP NIOs. + + :returns: base UDP port value (integer) + """ + + return self._baseudp + + @baseudp.setter + def baseudp(self, baseudp): + """ + Set the next open UDP port for NIOs for this hypervisor. + + :param baseudp: base UDP port value (integer) + """ + + self._baseudp = baseudp + + @property + def ghosts(self): + """ + Returns a list of the ghosts hosted by this hypervisor. + + :returns: Ghosts dict (image_name -> device) + """ + + return self._ghosts + + def add_ghost(self, image_name, router): + """ + Adds a ghost name to the list of ghosts created on this hypervisor. + + :param image_name: name of the ghost image + :param router: router object + """ + + self._ghosts[image_name] = router + + @property + def jitsharing_groups(self): + """ + Returns a list of the JIT sharing groups hosted by this hypervisor. + + :returns: JIT sharing groups dict (image_name -> group number) + """ + + return self._ghosts + + def add_jitsharing_group(self, image_name, group_number): + """ + Adds a JIT blocks sharing group name to the list of groups created on this hypervisor. + + :param image_name: name of the ghost image + :param group_number: group (integer) + """ + + self._jitsharing_groups[image_name] = group_number + + @property + def host(self): + """ + Returns this hypervisor host. + + :returns: host (string) + """ + + return self._host + + @property + def port(self): + """ + Returns this hypervisor port. + + :returns: port (integer) + """ + + return self._port + + def send_raw(self, string): + """ + Sends a raw command to this hypervisor. Use sparingly. + + :param string: command string. + + :returns: command result (string) + """ + + result = self.send(string) + return result + + 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' + + if not self._socket: + raise DynamipsError("Not connected") + + try: + command = command.strip() + '\n' + log.debug("sending {}".format(command)) + self.socket.sendall(command.encode('utf-8')) + except socket.error as e: + raise DynamipsError("Lost communication with {host}:{port} :{error}" + .format(host=self._host, port=self._port, error=e)) + + # Now retrieve the result + data = [] + buf = '' + while True: + try: + chunk = self.socket.recv(1024) # match to Dynamips' buffer size + buf += chunk.decode("utf-8") + except socket.error as e: + raise DynamipsError("Communication timed out with {host}:{port} :{error}" + .format(host=self._host, port=self._port, error=e)) + + # 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}" + .format(host=self._host, port=self._port)) + + data += buf.split('\r\n') + if data[-1] == '': + data.pop() + buf = '' + + if len(data) == 0: + raise DynamipsError("no data returned from {host}:{port}" + .format(host=self._host, port=self._port)) + + # 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 diff --git a/gns3server/modules/dynamips/hypervisor.py b/gns3server/modules/dynamips/hypervisor.py new file mode 100644 index 00000000..3a8933f3 --- /dev/null +++ b/gns3server/modules/dynamips/hypervisor.py @@ -0,0 +1,270 @@ +# -*- 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 . + +""" +Represents a Dynamips hypervisor and starts/stops the associated Dynamips process. +""" + +import os +import subprocess +import logging + +from .dynamips_hypervisor import DynamipsHypervisor + +logger = logging.getLogger(__name__) + + +class Hypervisor(DynamipsHypervisor): + """ + Hypervisor. + + :param path: path to Dynamips executable + :param workingdir: working directory + :param port: port for this hypervisor + :param host: host/address for this hypervisor + """ + + _instance_count = 0 + + def __init__(self, path, workingdir, host, port): + + DynamipsHypervisor.__init__(self, host, port) + + # create an unique ID + self._id = Hypervisor._instance_count + Hypervisor._instance_count += 1 + + self._path = path + self._workingdir = workingdir + self._command = [] + self._process = None + self._stdout = None + + # settings used the load-balance hypervisors + # (for the hypervisor manager) + self._memory_load = 0 + self._ios_image_ref = "" + + @property + def id(self): + """ + Returns the unique ID for this hypervisor. + + :returns: id (integer) + """ + + return(self._id) + + @property + def path(self): + """ + Returns the path to the Dynamips executable. + + :returns: path to Dynamips + """ + + return(self._path) + + @path.setter + def path(self, path): + """ + Set the path to the Dynamips executable. + + :param path: path to Dynamips + """ + + self._path = path + + @property + def port(self): + """ + Returns the port used to start the Dynamips hypervisor. + + :returns: port number (integer) + """ + + return(self._port) + + @port.setter + def port(self, port): + """ + Set the port used to start the Dynamips hypervisor. + + :param port: port number (integer) + """ + + self._port = port + + @property + def host(self): + """ + Returns the host (binding) used to start the Dynamips hypervisor. + + :returns: host/address (string) + """ + + return(self._host) + + @host.setter + def host(self, host): + """ + Set the host (binding) used to start the Dynamips hypervisor. + + :param host: host/address (string) + """ + + self._host = host + + @property + def workingdir(self): + """ + Returns the working directory used to start the Dynamips hypervisor. + + :returns: path to a working directory + """ + + return(self._workingdir) + + @workingdir.setter + def workingdir(self, workingdir): + """ + Set the working directory used to start the Dynamips hypervisor. + + :param workingdir: path to a working directory + """ + + self._workingdir = workingdir + + @property + def image_ref(self): + """ + Returns the reference IOS image name + (used by the hypervisor manager for load-balancing purposes). + + :returns: image reference name + """ + + return self._ios_image_ref + + @image_ref.setter + def image_ref(self, ios_image_name): + """ + Set the reference IOS image name + (used by the hypervisor manager for load-balancing purposes). + + :param ios_image_name: image reference name + """ + + self._ios_image_ref = ios_image_name + + def increase_memory_load(self, memory): + """ + Increases the memory load of this hypervisor. + (used by the hypervisor manager for load-balancing purposes). + + :param memory: amount of RAM (integer) + """ + + self._memory_load += memory + + def decrease_memory_load(self, memory): + """ + Decreases the memory load of this hypervisor. + (used by the hypervisor manager for load-balancing purposes). + + :param memory: amount of RAM (integer) + """ + + self._memory_load -= memory + + @property + def memory_load(self): + """ + Returns the memory load of this hypervisor. + (used by the hypervisor manager for load-balancing purposes). + + :returns: amount of RAM (integer) + """ + + return self._memory_load + + def start(self): + """ + Starts the Dynamips hypervisor process. + """ + + self._command = self._build_command() + try: + logger.info("Starting Dynamips: {}".format(self._command)) + # TODO: create unique filename for stdout + self.stdout_file = os.path.join(self._workingdir, "dynamips.log") + fd = open(self.stdout_file, "w") + # TODO: check for exceptions and if process has already been started + self._process = subprocess.Popen(self._command, + stdout=fd, + stderr=subprocess.STDOUT, + cwd=self._workingdir) + logger.info("Dynamips started PID={}".format(self._process.pid)) + except OSError as e: + logger.error("Could not start Dynamips: {}".format(e)) + finally: + fd.close() + + def stop(self): + """ + Stops the Dynamips hypervisor process. + """ + + if self.is_running(): + logger.info("Stopping Dynamips PID={}".format(self._process.pid)) + self._process.kill() + + def read_stdout(self): + """ + Reads the standard output of the Dynamips process. + Only use when the process has been stopped or has crashed. + """ + + # TODO: check for exceptions + with open(self.stdout_file) as file: + output = file.read() + return output + + def is_running(self): + """ + Checks if the process is running + + :returns: True or False + """ + + if self._process and self._process.poll() == 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 + if self._host != '0.0.0.0': + command.extend(['-H', self._host + ':' + str(self._port)]) + else: + command.extend(['-H', self._port]) + return command diff --git a/gns3server/modules/dynamips/hypervisor_manager.py b/gns3server/modules/dynamips/hypervisor_manager.py new file mode 100644 index 00000000..d759cd1b --- /dev/null +++ b/gns3server/modules/dynamips/hypervisor_manager.py @@ -0,0 +1,235 @@ +# -*- 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 . + +""" +Manages Dynamips hypervisors (load-balancing etc.) +""" + +from __future__ import unicode_literals +from .hypervisor import Hypervisor +import socket +import time +import logging + +log = logging.getLogger(__name__) + + +class HypervisorManager(object): + """ + Manages Dynamips hypervisors. + + :param path: path to the Dynamips executable + :param workingdir: path to a working directory + :param host: host/address for hypervisors to listen to + :param base_port: base TCP port for hypervisors + :param base_console: base TCP port for consoles + :param base_aux: base TCP port for auxiliary consoles + :param base_udp: base UDP port for UDP tunnels + """ + + def __init__(self, + path, + workingdir, + host='127.0.0.1', + base_port=7200, + base_console=2000, + base_aux=3000, + base_udp=10000): + + self._hypervisors = [] + self._path = path + self._workingdir = workingdir + self._base_port = base_port + self._current_port = self._base_port + self._base_console = base_console + self._base_aux = base_aux + self._base_udp = base_udp + self._host = host + self._clean_workingdir = False + self._ghost_ios = True + self._mmap = True + self._jit_sharing = False + self._sparsemem = True + self._memory_usage_limit_per_hypervisor = 1024 + self._group_ios_per_hypervisor = True + + def __del__(self): + """ + Shutdowns all started hypervisors + """ + + self.stop_all_hypervisors() + + @property + def hypervisors(self): + """ + Returns all hypervisor instances. + + :returns: list of hypervisor objects + """ + + return self._hypervisors + + @property + def memory_usage_limit_per_hypervisor(self): + """ + Returns the memory usage limit per hypervisor + + :returns: limit value (integer) + """ + + return self._memory_usage_limit_per_hypervisor + + @memory_usage_limit_per_hypervisor.setter + def memory_usage_limit_per_hypervisor(self, memory_limit): + """ + Set the memory usage limit per hypervisor + + :param memory_limit: memory limit value (integer) + """ + + self._memory_usage_limit_per_hypervisor = memory_limit + + @property + def group_ios_per_hypervisor(self): + """ + Returns if router are grouped per hypervisor + based on their IOS image. + + :returns: True or False + """ + + return self._group_ios_per_hypervisor + + @group_ios_per_hypervisor.setter + def group_ios_per_hypervisor(self, value): + """ + Set if router are grouped per hypervisor + based on their IOS image. + + :param value: True or False + """ + + self._group_ios_per_hypervisor = value + + def wait_for_hypervisor(self, host, port, timeout=10): + """ + Waits for an hypervisor to be started (accepting a socket connection) + + :param host: host/address to connect to the hypervisor + :param port: port to connect to the hypervisor + :param timeout: timeout value (default is 10 seconds) + """ + + # try to connect 5 times + for _ in range(0, 5): + try: + s = socket.create_connection((host, port), timeout) + except socket.error as e: + time.sleep(0.5) + last_exception = e + continue + connection_success = True + break + + if connection_success: + s.close() + #time.sleep(0.1) + else: + log.critical("Couldn't connect to hypervisor on {}:{} :{}".format(host, port, + last_exception)) + + def start_new_hypervisor(self): + """ + Creates a new Dynamips process and start it. + + :returns: the new hypervisor object + """ + + hypervisor = Hypervisor(self._path, + self._workingdir, + self._host, + self._current_port) + + log.info("creating new hypervisor {}:{}".format(hypervisor.host, hypervisor.port)) + hypervisor.start() + + self.wait_for_hypervisor(self._host, self._current_port) + log.info("hypervisor {}:{} has successfully started".format(hypervisor.host, hypervisor.port)) + + hypervisor.connect() + self._hypervisors.append(hypervisor) + self._current_port += 1 + return hypervisor + + def allocate_hypervisor_for_router(self, router_ios_image, router_ram): + """ + Allocates a Dynamips hypervisor for a specific router + (new or existing depending on the RAM amount and IOS image) + + :param router_ios_image: IOS image name + :param router_ram: amount of RAM (integer) + + :returns: the allocated hypervisor object + """ + + for hypervisor in self._hypervisors: + if self._group_ios_per_hypervisor and hypervisor.image_ref != router_ios_image: + continue + if (hypervisor.memory_load + router_ram) <= self._memory_usage_limit_per_hypervisor: + current_memory_load = hypervisor.memory_load + hypervisor.increase_memory_load(router_ram) + log.info("allocating existing hypervisor {}:{}, RAM={}+{}".format(hypervisor.host, + hypervisor.port, + current_memory_load, + router_ram)) + return hypervisor + + hypervisor = self.start_new_hypervisor() + hypervisor.image_ref = router_ios_image + hypervisor.increase_memory_load(router_ram) + return hypervisor + + def unallocate_hypervisor_for_router(self, router): + """ + Unallocates a Dynamips hypervisor for a specific router. + + :param router: router object + """ + + hypervisor = router.hypervisor + hypervisor.decrease_memory_load(router.ram) + + if hypervisor.memory_load < 0: + log.warn("hypervisor {}:{} has a memory load below 0 ({})".format(hypervisor.host, + hypervisor.port, + hypervisor.memory_load)) + hypervisor.memory_load = 0 + + # memory load at 0MB and no devices managed anymore... + # let's stop this hypervisor + if hypervisor.memory_load == 0 and not hypervisor.devices: + hypervisor.stop() + self._hypervisors.remove(hypervisor) + + def stop_all_hypervisors(self): + """ + Stops all hypervisors. + """ + + for hypervisor in self._hypervisors: + hypervisor.stop() diff --git a/gns3server/modules/dynamips/nios/__init__.py b/gns3server/modules/dynamips/nios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gns3server/modules/dynamips/nios/nio.py b/gns3server/modules/dynamips/nios/nio.py new file mode 100644 index 00000000..41346ce5 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio.py @@ -0,0 +1,236 @@ +# -*- 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 . + +""" +Base interface for Dynamips Network Input/Output (NIO) module ("nio"). +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L451 +""" + +from ..dynamips_error import DynamipsError + + +class NIO(object): + """ + Base NIO class + + :param hypervisor: Dynamips hypervisor object + """ + + def __init__(self, hypervisor): + + self._hypervisor = hypervisor + 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} + + def list(self): + """ + Returns all NIOs. + + :returns: NIO list + """ + + return self._hypervisor.send("nio list") + + def delete(self): + """ + Deletes this NIO. + """ + + self._hypervisor.send("nio delete {}".format(self._name)) + + def rename(self, new_name): + """ + Renames this NIO + + :param new_name: new NIO name + """ + + self._hypervisor.send("nio rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def debug(self, debug): + """ + Enables/Disables debugging for this NIO. + + :param debug: debug value (0 = disable, enable = 1) + """ + + self._hypervisor.send("nio set_debug {name} {debug}".format(name=self._name, + debug=debug)) + + 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] + + 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 + + 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] + + 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 + + def setup_filter(self, direction, options): + """ + Setups a packet filter binded with this NIO. + + Filter "freq_drop" has 1 argument "". It will drop + everything with a -1 frequency, drop every Nth packet with a + positive frequency, or drop nothing. + + Filter "capture" has 2 arguments " ". + 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] + + 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) + + def get_stats(self): + """ + Gets statistics for this NIO. + + :returns: NIO statistics (string with packets in, packets out, bytes in, bytes out) + """ + + return self._hypervisor.send("nio get_stats {}".format(self._name))[0] + + def reset_stats(self): + """ + Resets statistics for this NIO. + """ + + self._hypervisor.send("nio reset_stats {}".format(self._name)) + + def set_bandwidth(self, bandwidth): + """ + Set bandwidth constraint. + + :param bandwidth: bandwidth integer value (in Kb/s) + """ + + self._hypervisor.send("nio set_bandwidth {name} {bandwidth}".format(name=self._name, + bandwidth=bandwidth)) + self._bandwidth = bandwidth + + @property + def bandwidth(self): + """ + Returns the bandwidth constraint for this NIO. + + :returns: bandwidth integer value (in Kb/s) + """ + + return self._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 diff --git a/gns3server/modules/dynamips/nios/nio_fifo.py b/gns3server/modules/dynamips/nios/nio_fifo.py new file mode 100644 index 00000000..e4153f53 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_fifo.py @@ -0,0 +1,53 @@ +# -*- 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 . + +""" +Interface for FIFO NIOs. +""" + +from .nio import NIO + + +class NIO_FIFO(NIO): + """ + Dynamips FIFO NIO. + + :param hypervisor: Dynamips hypervisor object + """ + + _instance_count = 0 + + def __init__(self, hypervisor): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_FIFO._instance_count + NIO_FIFO._instance_count += 1 + self._name = 'nio_fifo' + str(self._id) + + self._hypervisor.send("nio create_fifo {}".format(self._name)) + + def crossconnect(self, nio): + """ + Establishes a cross-connect between this FIFO NIO and another one. + + :param nio: FIFO NIO object. + """ + + self._hypervisor.send("nio crossconnect_fifo {name} {nio}".format(name=self._name, + nio=nio)) diff --git a/gns3server/modules/dynamips/nios/nio_generic_ethernet.py b/gns3server/modules/dynamips/nios/nio_generic_ethernet.py new file mode 100644 index 00000000..48a2c9c5 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_generic_ethernet.py @@ -0,0 +1,56 @@ +# -*- 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 . + +""" +Interface for generic Ethernet NIOs (PCAP library). +""" + +from .nio import NIO + + +class NIO_GenericEthernet(NIO): + """ + Dynamips generic Ethernet NIO. + + :param hypervisor: Dynamips hypervisor object + :param ethernet_device: Ethernet device name (e.g. eth0) + """ + + _instance_count = 0 + + def __init__(self, hypervisor, ethernet_device): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_GenericEthernet._instance_count + NIO_GenericEthernet._instance_count += 1 + self._name = 'nio_gen_eth' + str(self._id) + self._ethernet_device = ethernet_device + + self._hypervisor.send("nio create_gen_eth {name} {eth_device}".format(name=self._name, + eth_device=ethernet_device)) + + @property + def ethernet_device(self): + """ + Returns the Ethernet device used by this NIO. + + :returns: the Ethernet device name + """ + + return self._ethernet_device diff --git a/gns3server/modules/dynamips/nios/nio_linux_ethernet.py b/gns3server/modules/dynamips/nios/nio_linux_ethernet.py new file mode 100644 index 00000000..afee21b9 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_linux_ethernet.py @@ -0,0 +1,56 @@ +# -*- 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 . + +""" +Interface for Linux Ethernet NIOs (Linux only). +""" + +from .nio import NIO + + +class NIO_LinuxEthernet(NIO): + """ + Dynamips Linux Ethernet NIO. + + :param hypervisor: Dynamips hypervisor object + :param ethernet_device: Ethernet device name (e.g. eth0) + """ + + _instance_count = 0 + + def __init__(self, hypervisor, ethernet_device): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_LinuxEthernet._instance_count + NIO_LinuxEthernet._instance_count += 1 + self._name = 'nio_linux_eth' + str(self._id) + self._ethernet_device = ethernet_device + + self._hypervisor.send("nio create_linux_eth {name} {eth_device}".format(name=self._name, + eth_device=ethernet_device)) + + @property + def ethernet_device(self): + """ + Returns the Ethernet device used by this NIO. + + :returns: the Ethernet device name + """ + + return self._ethernet_device diff --git a/gns3server/modules/dynamips/nios/nio_mcast.py b/gns3server/modules/dynamips/nios/nio_mcast.py new file mode 100644 index 00000000..6b80fd65 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_mcast.py @@ -0,0 +1,92 @@ +# -*- 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 . + +""" +Interface for multicast NIOs. +""" + +from .nio import NIO + + +class NIO_Mcast(NIO): + """ + Dynamips Linux Ethernet NIO. + + :param hypervisor: Dynamips hypervisor object + :param group: multicast group to bind + :param port: port for binding + """ + + _instance_count = 0 + + def __init__(self, hypervisor, group, port): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_Mcast._instance_count + NIO_Mcast._instance_count += 1 + self._name = 'nio_mcast' + str(self._id) + self._group = group + self._port = port + self._ttl = 1 # default TTL + + self._hypervisor.send("nio create_mcast {name} {mgroup} {mport}".format(name=self._name, + mgroup=group, + mport=port)) + + @property + def group(self): + """ + Returns the multicast group + + :returns: multicast group address + """ + + return self._group + + @property + def port(self): + """ + Returns the port + + :returns: port number + """ + + return self._port + + @property + def ttl(self): + """ + Returns the TTL associated with the multicast address. + + :returns: TTL value + """ + + return self._ttl + + @ttl.setter + def ttl(self, ttl): + """ + Set the TTL for the multicast address + + :param ttl: TTL value + """ + + self._hypervisor.send("nio set_mcast_ttl {name} {ttl}".format(name=self._name, + ttl=ttl)) + self._ttl = ttl diff --git a/gns3server/modules/dynamips/nios/nio_null.py b/gns3server/modules/dynamips/nios/nio_null.py new file mode 100644 index 00000000..7795e2c4 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_null.py @@ -0,0 +1,43 @@ +# -*- 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 . + +""" +Interface for dummy NIOs (mostly for tests). +""" + +from .nio import NIO + + +class NIO_Null(NIO): + """ + Dynamips NULL NIO. + + :param hypervisor: Dynamips hypervisor object + """ + + _instance_count = 0 + + def __init__(self, hypervisor): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_Null._instance_count + NIO_Null._instance_count += 1 + self._name = 'nio_null' + str(self._id) + + self._hypervisor.send("nio create_null {}".format(self._name)) diff --git a/gns3server/modules/dynamips/nios/nio_tap.py b/gns3server/modules/dynamips/nios/nio_tap.py new file mode 100644 index 00000000..28ce710d --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_tap.py @@ -0,0 +1,56 @@ +# -*- 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 . + +""" +Interface for TAP NIOs (UNIX based OSes only). +""" + +from .nio import NIO + + +class NIO_TAP(NIO): + """ + Dynamips TAP NIO. + + :param hypervisor: Dynamips hypervisor object + :param tap_device: TAP device name (e.g. tap0) + """ + + _instance_count = 0 + + def __init__(self, hypervisor, tap_device): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_TAP._instance_count + NIO_TAP._instance_count += 1 + self._name = 'nio_tap' + str(self._id) + self._tap_device = tap_device + + self._hypervisor.send("nio create_tap {name} {tap}".format(name=self._name, + tap=tap_device)) + + @property + def tap_device(self): + """ + Returns the TAP device used by this NIO. + + :returns: the TAP device name + """ + + return self._tap_device diff --git a/gns3server/modules/dynamips/nios/nio_udp.py b/gns3server/modules/dynamips/nios/nio_udp.py new file mode 100644 index 00000000..6021cfe9 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_udp.py @@ -0,0 +1,82 @@ +# -*- 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 . + +""" +Interface for UDP NIOs. +""" + +from .nio import NIO + + +class NIO_UDP(NIO): + """ + Dynamips UDP NIO. + + :param hypervisor: Dynamips hypervisor object + :param lport: local port number + :param rhost: remote address/host + :param rport: remote port number + """ + + _instance_count = 0 + + def __init__(self, hypervisor, lport, rhost, rport): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_UDP._instance_count + NIO_UDP._instance_count += 1 + self._name = 'nio_udp' + str(self._id) + self._lport = lport + self._rhost = rhost + self._rport = rport + + self._hypervisor.send("nio create_udp {name} {lport} {rhost} {rport}".format(name=self._name, + lport=lport, + rhost=rhost, + rport=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 diff --git a/gns3server/modules/dynamips/nios/nio_udp_auto.py b/gns3server/modules/dynamips/nios/nio_udp_auto.py new file mode 100644 index 00000000..eedbbcd5 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_udp_auto.py @@ -0,0 +1,106 @@ +# -*- 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 . + +""" +Interface for automatic UDP NIOs. +""" + +from .nio import NIO + + +class NIO_UDP_auto(NIO): + """ + Dynamips auto UDP NIO. + + :param hypervisor: Dynamips hypervisor object + :param laddr: local address + :param lport_start: start local port range + :param lport_end: end local port range + """ + + _instance_count = 0 + + def __init__(self, hypervisor, laddr, lport_start, lport_end): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_UDP_auto._instance_count + NIO_UDP_auto._instance_count += 1 + self._name = 'nio_udp_auto' + str(self._id) + + self._laddr = laddr + self._lport = int(self._hypervisor.send("nio create_udp_auto {name} {laddr} {lport_start} {lport_end}".format(name=self._name, + laddr=laddr, + lport_start=lport_start, + lport_end=lport_end))[0]) + self._raddr = None + self._rport = None + + @property + def laddr(self): + """ + Returns the local address + + :returns: local address + """ + + return self._laddr + + @property + def lport(self): + """ + Returns the local port + + :returns: local port number + """ + + return self._lport + + @property + def raddr(self): + """ + Returns the remote address + + :returns: remote address + """ + + return self._raddr + + @property + def rport(self): + """ + Returns the remote port + + :returns: remote port number + """ + + return self._rport + + def connect(self, raddr, rport): + """ + Connects this NIO to a remote socket + + :param raddr: remote address + :param rport: remote port number + """ + + self._hypervisor.send("nio connect_udp_auto {name} {raddr} {rport}".format(name=self._name, + raddr=raddr, + rport=rport)) + self._raddr = raddr + self._rport = rport diff --git a/gns3server/modules/dynamips/nios/nio_unix.py b/gns3server/modules/dynamips/nios/nio_unix.py new file mode 100644 index 00000000..304c7f72 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_unix.py @@ -0,0 +1,69 @@ +# -*- 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 . + +""" +Interface for UNIX NIOs (Unix based OSes only). +""" + +from .nio import NIO + + +class NIO_UNIX(NIO): + """ + Dynamips UNIX NIO. + + :param hypervisor: Dynamips hypervisor object + :param local_file: local UNIX socket filename + :param remote_file: remote UNIX socket filename + """ + + _instance_count = 0 + + def __init__(self, hypervisor, local_file, remote_file): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_UNIX._instance_count + NIO_UNIX._instance_count += 1 + self._name = 'nio_unix' + str(self._id) + self._local_file = local_file + self._remote_file = remote_file + + self._hypervisor.send("nio create_unix {name} {local} {remote}".format(name=self._name, + local=local_file, + remote=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 diff --git a/gns3server/modules/dynamips/nios/nio_vde.py b/gns3server/modules/dynamips/nios/nio_vde.py new file mode 100644 index 00000000..9f4df297 --- /dev/null +++ b/gns3server/modules/dynamips/nios/nio_vde.py @@ -0,0 +1,69 @@ +# -*- 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 . + +""" +Interface for VDE (Virtual Distributed Ethernet) NIOs (Unix based OSes only). +""" + +from .nio import NIO + + +class NIO_VDE(NIO): + """ + Dynamips VDE NIO. + + :param hypervisor: Dynamips hypervisor object + :param control_file: VDE control filename + :param local_file: VDE local filename + """ + + _instance_count = 0 + + def __init__(self, hypervisor, control_file, local_file): + + NIO.__init__(self, hypervisor) + + # create an unique ID + self._id = NIO_VDE._instance_count + NIO_VDE._instance_count += 1 + self._name = 'nio_vde' + str(self._id) + self._control_file = control_file + self._local_file = local_file + + self._hypervisor.send("nio create_vde {name} {control} {local}".format(name=self._name, + control=control_file, + local=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 diff --git a/gns3server/modules/dynamips/nodes/__init__.py b/gns3server/modules/dynamips/nodes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/gns3server/modules/dynamips/nodes/atm_bridge.py b/gns3server/modules/dynamips/nodes/atm_bridge.py new file mode 100644 index 00000000..1028249f --- /dev/null +++ b/gns3server/modules/dynamips/nodes/atm_bridge.py @@ -0,0 +1,170 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual ATM bridge module ("atm_bridge"). +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L622 +""" + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError + + +class ATMBridge(object): + """ + Dynamips bridge switch. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this switch + """ + + def __init__(self, hypervisor, name): + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._hypervisor.send("atm_bridge create {}".format(self._name)) + self._hypervisor.devices.append(self) + self._nios = {} + self._mapping = {} + + @property + def name(self): + """ + Returns the current name of this ATM bridge. + + :returns: ATM bridge name + """ + + return self._name[1:-1] # remove quotes + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all ATM bridge instances. + + :returns: list of all ATM bridges + """ + + return self._hypervisor.send("atm_bridge list") + + @property + def nios(self): + """ + Returns all the NIOs member of this ATM bridge. + + :returns: nio list + """ + + return self._nios + + @property + def mapping(self): + """ + Returns port mapping + + :returns: mapping list + """ + + return self._mapping + + def rename(self, new_name): + """ + Renames this ATM bridge. + + :param new_name: New name for this bridge + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("atm_bridge rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this ATM bridge. + """ + + self._hypervisor.send("atm_bridge delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def add_nio(self, nio, port): + """ + Adds a NIO as new port on ATM bridge. + + :param nio: NIO object to add + :param port: port to allocate for the NIO + """ + + if port in self._nios: + raise DynamipsError("Port {} isn't free".format(port)) + + self._nios[port] = nio + + def remove_nio(self, port): + """ + Removes the specified NIO as member of this ATM switch. + + :param port: allocated port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + del self._nios[port] + + def configure(self, eth_port, atm_port, atm_vpi, atm_vci): + """ + Configures this ATM bridge. + + :param eth_port: Ethernet port + :param atm_port: ATM port + :param atm_vpi: ATM VPI + :param atm_vci: ATM VCI + """ + + if eth_port not in self._nios: + raise DynamipsError("Ethernet port {} is not allocated".format(eth_port)) + + if atm_port not in self._nios: + raise DynamipsError("ATM port {} is not allocated".format(atm_port)) + + eth_nio = self._nios[eth_port] + atm_nio = self._nios[atm_port] + + self._hypervisor.send("atm_bridge configure {name} {eth_nio} {atm_nio} {vpi} {vci}".format(name=self._name, + eth_nio=eth_nio, + atm_nio=atm_nio, + vpi=atm_vpi, + vci=atm_vci)) + self._mapping[eth_port] = (atm_port, atm_vpi, atm_vci) + + def unconfigure(self): + """ + Unconfigures this ATM bridge. + """ + + self._hypervisor.send("atm_bridge unconfigure {}".format(self._name)) + del self._mapping diff --git a/gns3server/modules/dynamips/nodes/atm_switch.py b/gns3server/modules/dynamips/nodes/atm_switch.py new file mode 100644 index 00000000..99474912 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/atm_switch.py @@ -0,0 +1,248 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual ATM switch module ("atmsw"). +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L593 +""" + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError + + +class ATMSwitch(object): + """ + Dynamips ATM switch. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this switch + """ + + def __init__(self, hypervisor, name): + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._hypervisor.send("atmsw create {}".format(self._name)) + self._hypervisor.devices.append(self) + self._nios = {} + self._mapping = {} + + @property + def name(self): + """ + Returns the current name of this ATM switch. + + :returns: ATM switch name + """ + + return self._name[1:-1] # remove quotes + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all ATM switches instances. + + :returns: list of all ATM switches + """ + + return self._hypervisor.send("atmsw list") + + @property + def nios(self): + """ + Returns all the NIOs member of this ATM switch. + + :returns: nio list + """ + + return self._nios + + @property + def mapping(self): + """ + Returns port mapping + + :returns: mapping list + """ + + return self._mapping + + def rename(self, new_name): + """ + Renames this ATM switch. + + :param new_name: New name for this switch + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("atmsw rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this ATM switch. + """ + + self._hypervisor.send("atmsw delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def add_nio(self, nio, port): + """ + Adds a NIO as new port on ATM switch. + + :param nio: NIO object to add + :param port: port to allocate for the NIO + """ + + if port in self._nios: + raise DynamipsError("Port {} isn't free".format(port)) + + self._nios[port] = nio + + def remove_nio(self, port): + """ + Removes the specified NIO as member of this ATM switch. + + :param port: allocated port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + del self._nios[port] + + 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] + + 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)) + self._mapping[(port1, vpi1)] = (port2, vpi2) + + 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] + + 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)) + del self._mapping[(port1, vpi1)] + + 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] + + 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)) + self._mapping[(port1, vpi1, vci1)] = (port2, vpi2, vci2) + + 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] + + 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)) + del self._mapping[(port1, vpi1, vci1)] diff --git a/gns3server/modules/dynamips/nodes/bridge.py b/gns3server/modules/dynamips/nodes/bridge.py new file mode 100644 index 00000000..9e5a8af1 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/bridge.py @@ -0,0 +1,121 @@ +# -*- 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 . + +""" +Interface for Dynamips NIO bridge module ("nio_bridge"). +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L538 +""" + +from __future__ import unicode_literals + + +class Bridge(object): + """ + Dynamips bridge. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this bridge + """ + + def __init__(self, hypervisor, name): + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._hypervisor.send("nio_bridge create {}".format(self._name)) + self._hypervisor.devices.append(self) + self._nios = [] + + @property + def name(self): + """ + Returns the current name of this bridge. + + :returns: bridge name + """ + + return self._name[1:-1] # remove quotes + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all bridge instances. + + :returns: list of all bridges + """ + + return self._hypervisor.send("nio_bridge list") + + @property + def nios(self): + """ + Returns all the NIOs member of this bridge. + + :returns: nio list + """ + + return self._nios + + def rename(self, new_name): + """ + Renames this bridge. + + :param new_name: New name for this bridge + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("nio_bridge rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this bridge. + """ + + self._hypervisor.send("nio_bridge delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def add_nio(self, nio): + """ + Adds a NIO as new port on this bridge. + + :param nio: NIO object to add + """ + + self._hypervisor.send("nio_bridge add_nio {name} {nio}".format(name=self._name, + nio=nio)) + self._nios.append(nio) + + def remove_nio(self, nio): + """ + Removes the specified NIO as member of this bridge. + + :param nio: NIO object to remove + """ + + self._hypervisor.send("nio_bridge remove_nio {name} {nio}".format(name=self._name, + nio=nio)) + self._nios.remove(nio) diff --git a/gns3server/modules/dynamips/nodes/c1700.py b/gns3server/modules/dynamips/nodes/c1700.py new file mode 100644 index 00000000..96b4ec72 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c1700.py @@ -0,0 +1,125 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 1700 instances module ("c1700") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L428 +""" + +from __future__ import unicode_literals +from .router import Router +from ..adapters.c1700_mb_1fe import C1700_MB_1FE +from ..adapters.c1700_mb_wic1 import C1700_MB_WIC1 + + +class C1700(Router): + """ + Dynamips c1700 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + :param chassis: chassis for this router: + 1720, 1721, 1750, 1751 or 1760 (default = 1720). + 1710 is not supported. + """ + + def __init__(self, hypervisor, name, chassis="1720"): + Router.__init__(self, hypervisor, name, platform="c1700") + + # Set default values for this platform + self._ram = 64 + self._nvram = 32 + self._disk0 = 0 + self._disk1 = 0 + self._chassis = chassis + self._iomem = 15 # percentage + self._clock_divisor = 8 + + if chassis != "1720": + self.chassis = chassis + + self._setup_chassis() + + def list(self): + """ + Returns all c1700 instances + + :returns: c1700 instance list + """ + + return self._hypervisor.send("c1700 list") + + def _setup_chassis(self): + """ + Set 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 + + @chassis.setter + def chassis(self, chassis): + """ + Set the chassis. + + :param: chassis string: + 1720, 1721, 1750, 1751 or 1760 + """ + + self._hypervisor.send("c1700 set_chassis {name} {chassis}".format(name=self._name, + 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 + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c1700 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c2600.py b/gns3server/modules/dynamips/nodes/c2600.py new file mode 100644 index 00000000..eaf75cde --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c2600.py @@ -0,0 +1,135 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 2600 instances module ("c2600") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L404 +""" + +from __future__ import unicode_literals +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 + + +class C2600(Router): + """ + Dynamips c2600 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + :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, hypervisor, name, chassis="2610"): + Router.__init__(self, hypervisor, name, platform="c2600") + + # Set default values for this platform + self._ram = 64 + self._nvram = 128 + self._disk0 = 0 + self._disk1 = 0 + self._chassis = chassis + self._iomem = 15 # percentage + self._clock_divisor = 8 + + if chassis != "2610": + self.chassis = chassis + + self._setup_chassis() + + def list(self): + """ + Returns all c2600 instances + + :returns: c2600 instance list + """ + + return self._hypervisor.send("c2600 list") + + def _setup_chassis(self): + """ + Set 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 + + @chassis.setter + def chassis(self, chassis): + """ + Set the chassis. + + :param: chassis string: + 2610, 2611, 2620, 2621, 2610XM, 2611XM + 2620XM, 2621XM, 2650XM or 2651XM + """ + + self._hypervisor.send("c2600 set_chassis {name} {chassis}".format(name=self._name, + 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 + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c2600 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c2691.py b/gns3server/modules/dynamips/nodes/c2691.py new file mode 100644 index 00000000..d075bbd5 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c2691.py @@ -0,0 +1,79 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 2691 instances module ("c2691") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L387 +""" + +from __future__ import unicode_literals +from .router import Router +from ..adapters.gt96100_fe import GT96100_FE + + +class C2691(Router): + """ + Dynamips c2691 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + """ + + def __init__(self, hypervisor, name): + Router.__init__(self, hypervisor, name, platform="c2691") + + # Set default values for this platform + 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() + + def list(self): + """ + Returns all c2691 instances + + :returns: c2691 instance list + """ + + return self._hypervisor.send("c2691 list") + + @property + def iomem(self): + """ + Returns I/O memory size for this router. + + :returns: I/O memory size (integer) + """ + + return self._iomem + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c2691 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c3600.py b/gns3server/modules/dynamips/nodes/c3600.py new file mode 100644 index 00000000..7806da54 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c3600.py @@ -0,0 +1,121 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 3600 instances module ("c3600") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L366 +""" + +from __future__ import unicode_literals +from .router import Router +from ..adapters.leopard_2fe import Leopard_2FE + + +class C3600(Router): + """ + Dynamips c3600 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + :param chassis: chassis for this router: + 3620, 3640 or 3660 (default = 3640). + """ + + def __init__(self, hypervisor, name, chassis="3640"): + Router.__init__(self, hypervisor, name, platform="c3600") + + # Set default values for this platform + self._ram = 128 + self._nvram = 128 + self._disk0 = 0 + self._disk1 = 0 + self._iomem = 5 # percentage + self._chassis = chassis + self._clock_divisor = 4 + + if chassis != "3640": + self.chassis = chassis + + self._setup_chassis() + + def list(self): + """ + Returns all c3600 instances + + :returns: c3600 instance list + """ + + return self._hypervisor.send("c3600 list") + + def _setup_chassis(self): + """ + Set 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 + + @chassis.setter + def chassis(self, chassis): + """ + Set the chassis. + + :param: chassis string: 3620, 3640 or 3660 + """ + + self._hypervisor.send("c3600 set_chassis {name} {chassis}".format(name=self._name, + 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 + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c3600 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c3725.py b/gns3server/modules/dynamips/nodes/c3725.py new file mode 100644 index 00000000..1730b48d --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c3725.py @@ -0,0 +1,79 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 3725 instances module ("c3725") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L346 +""" + +from __future__ import unicode_literals +from .router import Router +from ..adapters.gt96100_fe import GT96100_FE + + +class C3725(Router): + """ + Dynamips c3725 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + """ + + def __init__(self, hypervisor, name): + Router.__init__(self, hypervisor, name, platform="c3725") + + # Set default values for this platform + 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() + + def list(self): + """ + Returns all c3725 instances. + + :returns: c3725 instance list + """ + + return self._hypervisor.send("c3725 list") + + @property + def iomem(self): + """ + Returns I/O memory size for this router. + + :returns: I/O memory size (integer) + """ + + return self._iomem + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c3725 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c3745.py b/gns3server/modules/dynamips/nodes/c3745.py new file mode 100644 index 00000000..1ed94a40 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c3745.py @@ -0,0 +1,79 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 3745 instances module ("c3745") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L326 +""" + +from __future__ import unicode_literals +from .router import Router +from ..adapters.gt96100_fe import GT96100_FE + + +class C3745(Router): + """ + Dynamips c3745 router. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + """ + + def __init__(self, hypervisor, name): + Router.__init__(self, hypervisor, name, platform="c3745") + + # Set default values for this platform + 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() + + def list(self): + """ + Returns all c3745 instances. + + :returns: c3745 instance list + """ + + return self._hypervisor.send("c3745 list") + + @property + def iomem(self): + """ + Returns I/O memory size for this router. + + :returns: I/O memory size (integer) + """ + + return self._iomem + + @iomem.setter + def iomem(self, iomem): + """ + Set I/O memory size for this router. + + :param iomem: I/O memory size + """ + + self._hypervisor.send("c3745 set_iomem {name} {size}".format(name=self._name, + size=iomem)) + self._iomem = iomem diff --git a/gns3server/modules/dynamips/nodes/c7200.py b/gns3server/modules/dynamips/nodes/c7200.py new file mode 100644 index 00000000..baa981c6 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/c7200.py @@ -0,0 +1,187 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Cisco 7200 instances module ("c7200") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L294 +""" + + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError +from .router import Router +from ..adapters.c7200_io_2fe import C7200_IO_2FE +from ..adapters.c7200_io_ge_e import C7200_IO_GE_E + + +class C7200(Router): + """ + Dynamips c7200 router (model is 7206). + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + :param npe: default NPE + """ + + def __init__(self, hypervisor, name, npe="npe-400"): + Router.__init__(self, hypervisor, name, platform="c7200") + + # Set default values for this platform + self._ram = 256 + self._nvram = 128 + self._disk0 = 64 + self._disk1 = 0 + self._npe = npe + self._midplane = "vxr" + self._clock_divisor = 4 + + if npe != "npe-400": + 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) + + # first slot is a mandatory Input/Output controller (based on NPE type) + if npe == "npe-g2": + self._slots[0] = C7200_IO_GE_E() + else: + self._slots[0] = C7200_IO_2FE() + + def list(self): + """ + Returns all c7200 instances. + + :returns: c7200 instance list + """ + + return self._hypervisor.send("c7200 list") + + @property + def npe(self): + """ + Returns the NPE model. + + :returns: NPE model string (e.g. "npe-200") + """ + + return self._npe + + @npe.setter + def npe(self, npe): + """ + Set 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 self.is_running(): + raise DynamipsError("Cannot change NPE on running router") + + self._hypervisor.send("c7200 set_npe {name} {npe}".format(name=self._name, + 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 + + @midplane.setter + def midplane(self, midplane): + """ + Set the midplane model. + + :returns: midplane model string (e.g. "vxr" or "std") + """ + + self._hypervisor.send("c7200 set_midplane {name} {midplane}".format(name=self._name, + 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 + + @sensors.setter + def sensors(self, sensors): + """ + Set 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: + self._hypervisor.send("c7200 set_temp_sensor {name} {sensor_id} {temp}".format(name=self._name, + sensor_id=sensor_id, + temp=sensor)) + 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 + + @power_supplies.setter + def power_supplies(self, power_supplies): + """ + Set 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: + 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)) + power_supply_id += 1 + self._power_supplies = power_supplies diff --git a/gns3server/modules/dynamips/nodes/ethernet_switch.py b/gns3server/modules/dynamips/nodes/ethernet_switch.py new file mode 100644 index 00000000..b0bde746 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/ethernet_switch.py @@ -0,0 +1,212 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Ethernet switch module ("ethsw"). +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L558 +""" + + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError + + +class EthernetSwitch(object): + """ + Dynamips Ethernet switch. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this switch + """ + + def __init__(self, hypervisor, name): + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._hypervisor.send("ethsw create {}".format(self._name)) + self._hypervisor.devices.append(self) + self._nios = {} + self._mapping = {} + + @property + def name(self): + """ + Returns the current name of this Ethernet switch. + + :returns: Ethernet switch name + """ + + return self._name[1:-1] # remove quotes + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all Ethernet switches instances. + + :returns: list of all Ethernet switches + """ + + return self._hypervisor.send("ethsw list") + + @property + def nios(self): + """ + Returns all the NIOs member of this Ethernet switch. + + :returns: nio list + """ + + return self._nios + + @property + def mapping(self): + """ + Returns port mapping + + :returns: mapping list + """ + + return self._mapping + + def rename(self, new_name): + """ + Renames this Ethernet switch. + + :param new_name: New name for this switch + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("ethsw rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this Ethernet switch. + """ + + self._hypervisor.send("ethsw delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def add_nio(self, nio, port): + """ + Adds a NIO as new port on Ethernet switch. + + :param nio: NIO object to add + :param port: port to allocate for the NIO + """ + + if port in self._nios: + raise DynamipsError("Port {} isn't free".format(port)) + + self._hypervisor.send("ethsw add_nio {name} {nio}".format(name=self._name, + nio=nio)) + self._nios[port] = nio + + def remove_nio(self, port): + """ + Removes the specified NIO as member of this Ethernet switch. + + :param port: allocated port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + nio = self._nios[port] + self._hypervisor.send("ethsw remove_nio {name} {nio}".format(name=self._name, + nio=nio)) + del self._nios[port] + + if port in self._mapping: + del self._mapping[port] + + def set_access_port(self, port, vlan_id): + """ + Set the specified port as an ACCESS port. + + :param port: allocated port + :param vlan_id: VLAN number membership + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + nio = self._nios[port] + self._hypervisor.send("ethsw set_access_port {name} {nio} {vlan_id}".format(name=self._name, + nio=nio, + vlan_id=vlan_id)) + self._mapping[port] = ("access", vlan_id) + + def set_dot1q_port(self, port, native_vlan): + """ + Set the specified port as a 802.1Q trunk port. + + :param port: allocated port + :param native_vlan: native VLAN for this trunk port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + nio = self._nios[port] + self._hypervisor.send("ethsw set_dot1q_port {name} {nio} {native_vlan}".format(name=self._name, + nio=nio, + native_vlan=native_vlan)) + self._mapping[port] = ("dot1q", native_vlan) + + def set_qinq_port(self, port, outer_vlan): + """ + Set the specified port as a trunk (QinQ) port. + + :param port: allocated port + :param outer_vlan: outer VLAN (transport VLAN) for this QinQ port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + nio = self._nios[port] + self._hypervisor.send("ethsw set_qinq_port {name} {nio} {outer_vlan}".format(name=self._name, + nio=nio, + outer_vlan=outer_vlan)) + self._mapping[port] = ("qinq", outer_vlan) + + def get_mac_addr_table(self): + """ + Returns the MAC address table for this Ethernet switch. + + :returns: list of entries (Ethernet address, VLAN, NIO) + """ + + return self._hypervisor.send("ethsw show_mac_addr_table {}".format(self._name)) + + def clear_mac_addr_table(self): + """ + Clears the MAC address table for this Ethernet switch. + """ + + self._hypervisor.send("ethsw clear_mac_addr_table {}".format(self._name)) diff --git a/gns3server/modules/dynamips/nodes/frame_relay_switch.py b/gns3server/modules/dynamips/nodes/frame_relay_switch.py new file mode 100644 index 00000000..d27f282a --- /dev/null +++ b/gns3server/modules/dynamips/nodes/frame_relay_switch.py @@ -0,0 +1,188 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Frame-Relay switch module. +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L642 +""" + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError + + +class FrameRelaySwitch(object): + """ + Dynamips Frame Relay switch. + + :param hypervisor: Dynamips hypervisor object + :param name: name for this switch + """ + + def __init__(self, hypervisor, name): + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._hypervisor.send("frsw create {}".format(self._name)) + self._hypervisor.devices.append(self) + self._nios = {} + self._mapping = {} + + @property + def name(self): + """ + Returns the current name of this Frame Relay switch. + + :returns: Frame Relay switch name + """ + + return self._name[1:-1] # remove quotes + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all Frame Relay switches instances. + + :returns: list of all Frame Relay switches + """ + + return self._hypervisor.send("frsw list") + + @property + def nios(self): + """ + Returns all the NIOs member of this Frame Relay switch. + + :returns: nio list + """ + + return self._nios + + @property + def mapping(self): + """ + Returns port mapping + + :returns: mapping list + """ + + return self._mapping + + def rename(self, new_name): + """ + Renames this Frame Relay switch. + + :param new_name: New name for this switch + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("frsw rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this Frame Relay switch. + """ + + self._hypervisor.send("frsw delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def add_nio(self, nio, port): + """ + Adds a NIO as new port on Frame Relay switch. + + :param nio: NIO object to add + :param port: port to allocate for the NIO + """ + + if port in self._nios: + raise DynamipsError("Port {} isn't free".format(port)) + + self._nios[port] = nio + + def remove_nio(self, port): + """ + Removes the specified NIO as member of this Frame Relay switch. + + :param port: allocated port + """ + + if port not in self._nios: + raise DynamipsError("Port {} is not allocated".format(port)) + + del self._nios[port] + + 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] + + 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)) + self._mapping[(port1, dlci1)] = (port2, dlci2) + + 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] + + 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)) + del self._mapping[(port1, dlci1)] diff --git a/gns3server/modules/dynamips/nodes/hub.py b/gns3server/modules/dynamips/nodes/hub.py new file mode 100644 index 00000000..ff3f8c41 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/hub.py @@ -0,0 +1,76 @@ +# -*- 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 . + +""" +Hub object that uses the Bridge interface to create a hub with ports. +""" + +from __future__ import unicode_literals +from .bridge import Bridge +from ..dynamips_error import DynamipsError + + +class Hub(Bridge): + """ + Dynamips hub (based on Bridge) + + :param hypervisor: Dynamips hypervisor object + :param name: name for this hub + """ + + def __init__(self, hypervisor, name): + + Bridge.__init__(self, hypervisor, name) + self._mapping = {} + + @property + def mapping(self): + """ + Returns port mapping + + :returns: mapping list + """ + + return self._mapping + + def add_nio(self, nio, port): + """ + Adds a NIO as new port on this hub. + + :param nio: NIO object to add + :param port: port to allocate for the NIO + """ + + if port in self._mapping: + raise DynamipsError("Port {} isn't free".format(port)) + + Bridge.add_nio(self, nio) + self._mapping[port] = nio + + def remove_nio(self, port): + """ + Removes the specified NIO as member of this hub. + + :param port: allocated port + """ + + if port not in self._mapping: + raise DynamipsError("Port {} is not allocated".format(port)) + + nio = self._mapping[port] + Bridge.remove_nio(self, nio) + del self._mapping[port] diff --git a/gns3server/modules/dynamips/nodes/router.py b/gns3server/modules/dynamips/nodes/router.py new file mode 100644 index 00000000..16259299 --- /dev/null +++ b/gns3server/modules/dynamips/nodes/router.py @@ -0,0 +1,1085 @@ +# -*- 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 . + +""" +Interface for Dynamips virtual Machine module ("vm") +http://github.com/GNS3/dynamips/blob/master/README.hypervisor#L77 +""" + +from __future__ import unicode_literals +from ..dynamips_error import DynamipsError +import os + + +class Router(object): + """ + Dynamips router + + :param hypervisor: Dynamips hypervisor object + :param name: name for this router + :param platform: c7200, c3745, c3725, c3600, c2691, c2600 or c1700 + :param console_flag: create console ports if True. + """ + + _instance_count = 0 + _status = {0: "inactive", + 1: "shutting down", + 2: "running", + 3: "suspended"} + + def __init__(self, hypervisor, name, platform="c7200", console_flag=True): + + # create an unique ID + self._id = Router._instance_count + Router._instance_count += 1 + + self._hypervisor = hypervisor + self._name = '"' + name + '"' # put name into quotes to protect spaces + self._platform = platform + self._image = "" + self._ram = 128 # Megabytes + self._nvram = 128 # Kilobytes + self._mmap = True + self._sparsemem = True + self._clock_divisor = 8 + self._idlepc = "" + self._idlemax = 1500 + self._idlesleep = 30 + self._ghost_file = "" + self._ghost_status = 0 + self._exec_area = None # Megabytes (None = default for the router platform) + self._jit_sharing_group = None + self._disk0 = 0 # Megabytes + self._disk1 = 0 # Megabytes + self._confreg = '0x2102' + self._console = None + self._aux = None + self._mac_addr = None + self._system_id = None # processor board ID in IOS + self._slots = [] + + self._hypervisor.send("vm create {name} {id} {platform}".format(name=self._name, + id=self._id, + platform=self._platform)) + + if console_flag: + self.console = self._hypervisor.baseconsole + self._id + self.aux = self._hypervisor.baseaux + self._id + + self._hypervisor.devices.append(self) + + @property + def id(self): + """ + Returns the unique ID for this router. + + :returns: id (integer) + """ + + return self._id + + @property + def name(self): + """ + Returns the name of this router. + + :returns: name (string) + """ + + return self._name[1:-1] # remove quotes + + @property + def platform(self): + """ + Returns the platform of this router. + + :returns: platform name (string): + c7200, c3745, c3725, c3600, c2691, c2600 or c1700 + """ + + return self._platform + + @property + def hypervisor(self): + """ + Returns the current hypervisor. + + :returns: hypervisor object + """ + + return self._hypervisor + + def list(self): + """ + Returns all VM instances + + :returns: list of all VM instances + """ + + return self._hypervisor.send("vm list") + + def list_con_ports(self): + """ + Returns all VM console TCP ports + + :returns: list of port numbers + """ + + return self._hypervisor.send("vm list_con_ports") + + def rename(self, new_name): + """ + Renames this router. + + :param new_name: new name string + """ + + new_name = '"' + new_name + '"' # put the new name into quotes to protect spaces + self._hypervisor.send("vm rename {name} {new_name}".format(name=self._name, + new_name=new_name)) + self._name = new_name + + def delete(self): + """ + Deletes this router. + """ + + self._hypervisor.send("vm delete {}".format(self._name)) + self._hypervisor.devices.remove(self) + + def start(self): + """ + Starts this router. + At least the IOS image must be set. + """ + + self._hypervisor.send("vm start {}".format(self._name)) + + def stop(self): + """ + Stops this router. + The settings are kept. + """ + + self._hypervisor.send("vm stop {}".format(self._name)) + + def suspend(self): + """ + Suspends this router + """ + + self._hypervisor.send("vm suspend {}".format(self._name)) + + def resume(self): + """ + Resumes this suspended router + """ + + self._hypervisor.send("vm resume {}".format(self._name)) + + def get_status(self): + """ + Returns the status of this router + + :returns: 0=inactive, 1=shutting down, 2=running, 3=suspended + """ + + status_id = int(self._hypervisor.send("vm get_status {}".format(self._name))[0]) + return self._status[status_id] + + def is_running(self): + """ + Checks if this router is running. + + :returns: True if running, False otherwise + """ + + if self.get_status() == "running": + return True + return False + + @property + def jit_sharing_group(self): + """ + Returns the JIT sharing group for this router. + + :returns: translation sharing group ID + """ + + return self._jit_sharing_group + + @jit_sharing_group.setter + def jit_sharing_group(self, group_id): + """ + Set the translation sharing group (unstable). + + :param group_id: translation sharing group ID + """ + + if not self._image: + raise DynamipsError("Register an IOS image fist") + + self._hypervisor.send("vm set_tsg {name} {group_id}".format(name=self._name, + group_id=group_id)) + + self._jit_sharing_group = group_id + self._hypervisor.add_jitsharing_group(os.path.basename(self._image), group_id) + + def set_debug_level(self, level): + """ + Set the debug level for this router (default is 0). + + :param level: level number + """ + + self._hypervisor.send("vm set_debug_level {name} {level}".format(name=self._name, + level=level)) + + @property + def image(self): + """ + Returns this IOS image for this router. + + :returns: path to IOS image file + """ + + return self._image + + @image.setter + def image(self, image): + """ + Set the IOS image for this router. + There is no default. + + :param image: path to IOS image file + """ + + # encase image in quotes to protect spaces in the path + self._hypervisor.send("vm set_ios {name} {image}".format(name=self._name, + image='"' + image + '"')) + self._image = image + + def set_config(self, startup_config, private_config=''): + """ + Set the config files that are pushed to startup-config and + private-config in NVRAM when the instance is started. + + :param startup_config: path to statup-config file + :param private_config: path to private-config file + (keep existing data when if an empty string) + """ + + self._hypervisor.send("vm set_config {name} {startup} {private}".format(name=self._name, + startup='"' + startup_config + '"', + private='"' + private_config + '"')) + + def extract_config(self): + """ + Gets the contents of the config files + startup-config and private-config from NVRAM. + + :returns: tuple (startup-config, private-config) base64 encoded + """ + + try: + reply = self._hypervisor.send("vm extract_config {}".format(self._name))[0].rsplit(' ', 2)[-2:] + except IOError: + #for some reason Dynamips gets frozen when it does not find the magic number in the NVRAM file. + return (None, None) + startup_config = reply[0][1:-1] # get statup-config and remove single quotes + private_config = reply[1][1:-1] # get private-config and remove single quotes + return (startup_config, private_config) + + def push_config(self, startup_config, private_config='(keep)'): + """ + Pushes configuration to the config files startup-config and private-config in NVRAM. + The data is a Base64 encoded string, or '(keep)' to keep existing data. + + :param startup_config: statup-config string base64 encoded + :param private_config: private-config string base64 encoded + (keep existing data when if the value is ('keep')) + """ + + self._hypervisor.send("vm push_config {name} {startup} {private}".format(name=self._name, + startup=startup_config, + private=private_config)) + + @property + def ram(self): + """ + Returns the amount of RAM allocated to this router. + + :returns: amount of RAM in Mbytes (integer) + """ + + return self._ram + + @ram.setter + def ram(self, ram): + """ + Set amount of RAM allocated to this router + + :param ram: amount of RAM in Mbytes (integer) + """ + + self._hypervisor.send("vm set_ram {name} {ram}".format(name=self._name, + ram=self._ram)) + self._hypervisor.decrease_memory_load(self._ram) + self._ram = ram + self._hypervisor.increase_memory_load(self._ram) + + @property + def nvram(self): + """ + Returns the mount of NVRAM allocated to this router. + + :returns: amount of NVRAM in Kbytes (integer) + """ + + return self._nvram + + @nvram.setter + def nvram(self, nvram): + """ + Set amount of NVRAM allocated to this router + + :param nvram: amount of NVRAM in Kbytes (integer) + """ + + self._hypervisor.send("vm set_nvram {name} {nvram}".format(name=self._name, + nvram=self._nvram)) + self._nvram = nvram + + @property + def mmap(self): + """ + Returns True if a mapped file is used to simulate this router memory. + + :returns: boolean either mmap is activated or not + """ + + return self._mmap + + @mmap.setter + def mmap(self, mmap): + """ + Enable/Disable use of a mapped file to simulate router memory. + By default, a mapped file is used. This is a bit slower, but requires less memory. + + :param mmap: activate/deactivate mmap (boolean) + """ + + if mmap: + flag = 1 + else: + flag = 0 + self._hypervisor.send("vm set_ram_mmap {name} {mmap}".format(name=self._name, + mmap=flag)) + self._mmap = mmap + + @property + def sparsemem(self): + """ + Returns True if sparse memory is used on this router. + + :returns: boolean either mmap is activated or not + """ + + return self._sparsemem + + @sparsemem.setter + def sparsemem(self, sparsemem): + """ + Enable/disable use of sparse memory + + :param sparsemem: activate/deactivate sparsemem (boolean) + """ + + if sparsemem: + flag = 1 + else: + flag = 0 + self._hypervisor.send("vm set_sparse_mem {name} {sparsemem}".format(name=self._name, + sparsemem=flag)) + self._sparsemem = sparsemem + + @property + def clock_divisor(self): + """ + Returns the clock divisor value for this router. + + :returns: clock divisor value (integer) + """ + + return self._clock_divisor + + @clock_divisor.setter + def clock_divisor(self, clock_divisor): + """ + Set the clock divisor value. The higher is the value, the faster is the clock in the + virtual machine. The default is 4, but it is often required to adjust it. + + :param clock_divisor: clock divisor value (integer) + """ + + self._hypervisor.send("vm set_clock_divisor {name} {clock}".format(name=self._name, + clock=clock_divisor)) + self._clock_divisor = clock_divisor + + @property + def idlepc(self): + """ + Returns the idle Pointer Counter (PC). + + :returns: idlepc value (string) + """ + + return self._idlepc + + @idlepc.setter + def idlepc(self, idlepc): + """ + Set the idle Pointer Counter (PC) + + :param idlepc: idlepc value (string) + """ + + if not self.is_running(): + # router is not running + self._hypervisor.send("vm set_idle_pc {name} {idlepc}".format(name=self._name, + idlepc=idlepc)) + else: + self._hypervisor.send("vm set_idle_pc_online {name} 0 {idlepc}".format(name=self._name, + idlepc=idlepc)) + self._idlepc = idlepc + + def get_idle_pc_prop(self): + """ + Gets the idle PC proposals. + Takes 1000 measurements and records up to 10 idle PC proposals. + There is a 10ms wait between each measurement. + + :returns: list of idle PC proposal + """ + + return self._hypervisor.send("vm get_idle_pc_prop {} 0".format(self._name)) + + def show_idle_pc_prop(self): + """ + Dumps the idle PC proposals (previously generated). + + :returns: list of idle PC proposal + """ + + return self._hypervisor.send("vm show_idle_pc_prop {} 0".format(self._name)) + + @property + def idlemax(self): + """ + Returns CPU idle max value. + + :returns: idle max (integer) + """ + + return self._idlemax + + @idlemax.setter + def idlemax(self, idlemax): + """ + Set CPU idle max value + + :param idlemax: idle max value (integer) + """ + + if self.is_running(): # router is running + self._hypervisor.send("vm set_idle_max {name} 0 {idlemax}".format(name=self._name, + idlemax=idlemax)) + self._idlemax = idlemax + + @property + def idlesleep(self): + """ + Returns CPU idle sleep time value. + + :returns: idle sleep (integer) + """ + + return self._idlesleep + + @idlesleep.setter + def idlesleep(self, idlesleep): + """ + Set CPU idle sleep time value. + + :param idlesleep: idle sleep value (integer) + """ + + if self.is_running(): # router is running + self._hypervisor.send("vm set_idle_sleep_time {name} 0 {idlesleep}".format(name=self._name, + idlesleep=idlesleep)) + self._idlesleep = idlesleep + + def show_timer_drift(self): + """ + Shows info about potential timer drift. + + :returns: timer drift info. + """ + + return self._hypervisor.send("vm show_timer_drift {} 0".format(self._name)) + + @property + def ghost_file(self): + """ + Returns ghost RAM file. + + :returns: path to ghost file + """ + + return self._ghost_file + + @ghost_file.setter + def ghost_file(self, ghost_file): + """ + Set ghost RAM file + + :ghost_file: path to ghost file + """ + + self._hypervisor.send("vm set_ghost_file {name} {ghost_file}".format(name=self._name, + ghost_file=ghost_file)) + self._ghost_file = ghost_file + + # If this is a ghost instance, track this as a hosted ghost instance by this hypervisor + if self.ghost_status == 1: + self._hypervisor.add_ghost(ghost_file, self) + + def formatted_ghost_file(self): + """ + Returns a properly formatted ghost file name. + + :returns: formatted ghost_file name (string) + """ + + # Replace specials characters in 'drive:\filename' in Linux and Dynamips in MS Windows or viceversa. + ghost_file = os.path.basename(self._image) + '-' + self._hypervisor.host + '.ghost' + ghost_file = ghost_file.replace('\\', '-').replace('/', '-').replace(':', '-') + return ghost_file + + @property + def ghost_status(self): + """Returns ghost RAM status + + :returns: ghost status (integer) + """ + + return self._ghost_status + + @ghost_status.setter + def ghost_status(self, ghost_status): + """ + Set ghost RAM status + + :param ghost_status: state flag indicating status + 0 => Do not use IOS ghosting + 1 => This is a ghost instance + 2 => Use an existing ghost instance + """ + + self._hypervisor.send("vm set_ghost_status {name} {ghost_status}".format(name=self._name, + ghost_status=ghost_status)) + self._ghost_status = ghost_status + + @property + def exec_area(self): + """ + Returns the exec area value. + + :returns: exec area value (integer) + """ + + return self._exec_area + + @exec_area.setter + def exec_area(self, exec_area): + """ + Set the exec area value. + The exec area is a pool of host memory used to store pages + translated by the JIT (they contain the native code + corresponding to MIPS code pages). + + :param excec_area: exec area value (integer) + """ + + self._hypervisor.send("vm set_exec_area {name} {exec_area}".format(name=self._name, + exec_area=exec_area)) + self._exec_area = exec_area + + @property + def disk0(self): + """ + Returns the size (MB) for PCMCIA disk0. + + :returns: disk0 size (integer) + """ + + return self._disk0 + + @disk0.setter + def disk0(self, disk0): + """ + Set the size (MB) for PCMCIA disk0. + + :param disk0: disk0 size (integer) + """ + + self._hypervisor.send("vm set_disk0 {name} {disk0}".format(name=self._name, + disk0=disk0)) + self._disk0 = disk0 + + @property + def disk1(self): + """ + Returns the size (MB) for PCMCIA disk1. + + :returns: disk1 size (integer) + """ + + return self._disk1 + + @disk1.setter + def disk1(self, disk1): + """ + Set the size (MB) for PCMCIA disk1. + + :param disk1: disk1 size (integer) + """ + + self._hypervisor.send("vm set_disk1 {name} {disk1}".format(name=self._name, + disk1=disk1)) + self._disk1 = disk1 + + @property + def confreg(self): + """ + Returns the configuration register. + The default is 0x2102. + + :returns: configuration register value (string) + """ + + return self._confreg + + @confreg.setter + def confreg(self, confreg): + """ + Set the configuration register. + + :param confreg: configuration register value (string) + """ + + self._hypervisor.send("vm set_conf_reg {name} {confreg}".format(name=self._name, + confreg=confreg)) + self._confreg = confreg + + @property + def console(self): + """ + Returns the TCP console port. + + :returns: console port (integer) + """ + + return self._console + + @console.setter + def console(self, console): + """ + Set the TCP console port. + + :param console: console port (integer) + """ + + if console == self._console: + return + + self._hypervisor.send("vm set_con_tcp_port {name} {console}".format(name=self._name, + console=console)) + self._console = console + + @property + def aux(self): + """ + Returns the TCP auxiliary port. + + :returns: console auxiliary port (integer) + """ + + return self._aux + + @aux.setter + def aux(self, aux): + """ + Set the TCP auxiliary port. + + :param aux: console auxiliary port (integer) + """ + + if aux == self._aux: + return + + self._hypervisor.send("vm set_aux_tcp_port {name} {aux}".format(name=self._name, + aux=aux)) + self._aux = aux + + def get_cpu_info(self, cpu_id=0): + """ + Shows info about the CPU identified by cpu_id. + The boot CPU (which is typically the only CPU) has ID 0. + + :returns: ? (could not test) + """ + + # FIXME: nothing returned by Dynamips. + return self._hypervisor.send("vm cpu_info {name} {cpu_id}".format(name=self._name, + cpu_id=cpu_id)) + + def get_cpu_usage(self, cpu_id=0): + """ + Shows cpu usage in seconds, "cpu_id" is ignored. + + :returns: cpu usage in seconds + """ + + return int(self._hypervisor.send("vm cpu_usage {name} {cpu_id}".format(name=self._name, + cpu_id=cpu_id))[0]) + + def send_console_msg(self, message): + """ + Sends a message to the console. + + :param message: message to send to the console + """ + + self._hypervisor.send("vm send_con_msg {name} {message}".format(name=self._name, + message=message)) + + def send_aux_msg(self, message): + """ + Sends a message to the auxiliary console. + + :param message: message to send to the auxiliary console + """ + + self._hypervisor.send("vm send_aux_msg {name} {message}".format(name=self._name, + message=message)) + + @property + def mac_addr(self): + """ + Returns the MAC address. + + :returns: the MAC address (hexadecimal format: hh:hh:hh:hh:hh:hh) + """ + + return self._mac_addr + + @mac_addr.setter + def mac_addr(self, mac_addr): + """ + Set the MAC address. + + :param mac_addr: a MAC address (hexadecimal format: hh:hh:hh:hh:hh:hh) + """ + + self._hypervisor.send("{platform} set_mac_addr {name} {mac_addr}".format(platform=self._platform, + name=self._name, + mac_addr=mac_addr)) + self._mac_addr = mac_addr + + @property + def system_id(self): + """ + Returns the system ID. + + :returns: the system ID (also called board processor ID) + """ + + return self._system_id + + @system_id.setter + def system_id(self, system_id): + """ + Set the system ID. + + :param system_id: a system ID (also called board processor ID) + """ + + self._hypervisor.send("{platform} set_system_id {name} {system_id}".format(platform=self._platform, + name=self._name, + system_id=system_id)) + self._system_id = system_id + + def get_hardware_info(self): + """ + Get some hardware info about this router. + + :returns: ? (could not test) + """ + + # FIXME: nothing returned by Dynamips. + return (self._hypervisor.send("{platform} show_hardware {name}".format(platform=self._platform, + name=self._name))) + + def get_slot_bindings(self): + """ + Returns slot bindings. + + :returns: slot bindings (adapter names) list + """ + + return (self._hypervisor.send("vm slot_bindings {}".format(self._name))) + + def slot_add_binding(self, slot_id, adapter): + """ + Adds a slot binding. + + :param slot_id: slot ID + :param adapter: device to add in the corresponding slot (object) + """ + + try: + slot = self._slots[slot_id] + except IndexError: + raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name, + slot_id=slot_id)) + + if slot != None: + current_adapter = slot + raise DynamipsError("Slot {slot_id} is already occupied by adapter {adapter} on router {name}".format(name=self._name, + slot_id=slot_id, + adapter=current_adapter)) + + self._hypervisor.send("vm slot_add_binding {name} {slot_id} 0 {adapter}".format(name=self._name, + slot_id=slot_id, + adapter=adapter)) + self._slots[slot_id] = adapter + + # Generate an OIR event if the router is running and + # only for c7200, c3600 and c3745 (NM-4T only) + if self.is_running() and self._platform == 'c7200' \ + or (self._platform == 'c3600' and self.chassis == '3660') \ + or (self._platform == 'c3745' and adapter == 'NM-4T'): + + self._hypervisor.send("vm slot_oir_start {name} {slot_id} 0".format(name=self._name, + slot_id=slot_id)) + + def slot_remove_binding(self, slot_id): + """ + Removes a slot binding. + + :param slot_id: slot ID + """ + + try: + slot = self._slots[slot_id] + except IndexError: + raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name, + slot_id=slot_id)) + + if slot == None: + return + + # Generate an OIR event if the router is running and + # only for c7200, c3600 and c3745 (NM-4T only) + if self.is_running() and self._platform == 'c7200' \ + or (self._platform == 'c3600' and self.chassis == '3660') \ + or (self._platform == 'c3745' and slot == 'NM-4T'): + + self._hypervisor.send("vm slot_oir_stop {name} {slot_id} 0".format(name=self._name, + slot_id=slot_id)) + + self._hypervisor.send("vm slot_remove_binding {name} {slot_id} 0".format(name=self._name, + slot_id=slot_id)) + self._slots[slot_id] = None + + def install_wic(self, wic_slot_id, wic): + """ + Installs a WIC adapter into this router. + + :param wic_slot_id: WIC slot ID + :param wic: WIC to be install (object) + """ + + # WICs are always installed on adapters in slot 0 + slot_id = 0 + + # Do not check if slot has an adapter because adapters with WICs interfaces + # must be inserted by default in the router and cannot be removed. + adapter = self._slots[slot_id] + + if wic_slot_id > len(adapter.wics) - 1: + raise DynamipsError("WIC slot {wic_slot_id} doesn't exist".format(name=self._name, + wic_slot_id=wic_slot_id)) + + if not adapter.wic_slot_available(wic_slot_id): + raise DynamipsError("WIC slot {wic_slot_id} is already occupied by another WIC".format(name=self._name, + wic_slot_id=wic_slot_id)) + + # Dynamips WICs slot IDs start on a multiple of 16 + # WIC1 = 16, WIC2 = 32 and WIC3 = 48 + internal_wic_slot_id = 16 * (wic_slot_id + 1) + self._hypervisor.send("vm slot_add_binding {name} {slot_id} {wic_slot_id} {wic}".format(name=self._name, + slot_id=slot_id, + wic_slot_id=internal_wic_slot_id, + wic=wic)) + adapter.install_wic(wic_slot_id, wic) + + def uninstall_wic(self, wic_slot_id): + """ + Uninstalls a WIC adapter from this router. + + :param wic_slot_id: WIC slot ID + """ + + # WICs are always installed on adapters in slot 0 + slot_id = 0 + + # Do not check if slot has an adapter because adapters with WICs interfaces + # must be inserted by default in the router and cannot be removed. + adapter = self._slots[slot_id] + + if wic_slot_id > len(adapter.wics) - 1: + raise DynamipsError("WIC slot {wic_slot_id} doesn't exist".format(name=self._name, + wic_slot_id=wic_slot_id)) + + if adapter.wic_slot_available(wic_slot_id): + raise DynamipsError("No WIC is installed in WIC slot {wic_slot_id}".format(name=self._name, + wic_slot_id=wic_slot_id)) + # Dynamips WICs slot IDs start on a multiple of 16 + # WIC1 = 16, WIC2 = 32 and WIC3 = 48 + internal_wic_slot_id = 16 * (wic_slot_id + 1) + self._hypervisor.send("vm slot_remove_binding {name} {slot_id} {wic_slot_id}".format(name=self._name, + slot_id=slot_id, + wic_slot_id=internal_wic_slot_id)) + adapter.uninstall_wic(wic_slot_id) + + def get_slot_nio_bindings(self, slot_id): + """ + Returns slot NIO bindings. + + :param slot_id: slot ID + + :returns: list of NIO bindings + """ + + return (self._hypervisor.send("vm slot_nio_bindings {name} {slot_id}".format(name=self._name, + slot_id=slot_id))) + + def slot_add_nio_binding(self, slot_id, port_id, nio): + """ + Adds a slot NIO binding. + + :param slot_id: slot ID + :param port_id: port ID + :param nio: NIO to add to the slot/port (object) + """ + + try: + adapter = self._slots[slot_id] + except IndexError: + raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name, + slot_id=slot_id)) + if not adapter.port_exists(port_id): + raise DynamipsError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, + port_id=port_id)) + + self._hypervisor.send("vm slot_add_nio_binding {name} {slot_id} {port_id} {nio}".format(name=self._name, + slot_id=slot_id, + port_id=port_id, + nio=nio)) + self.slot_enable_nio(slot_id, port_id) + adapter.add_nio(port_id, nio) + + def slot_remove_nio_binding(self, slot_id, port_id): + """ + Removes a slot NIO binding. + + :param slot_id: slot ID + :param port_id: port ID + """ + + try: + adapter = self._slots[slot_id] + except IndexError: + raise DynamipsError("Slot {slot_id} doesn't exist on router {name}".format(name=self._name, + slot_id=slot_id)) + if not adapter.port_exists(port_id): + raise DynamipsError("Port {port_id} doesn't exist in adapter {adapter}".format(adapter=adapter, + port_id=port_id)) + + self.slot_disable_nio(slot_id, port_id) + self._hypervisor.send("vm slot_remove_binding {name} {slot_id} {port_id}".format(name=self._name, + slot_id=slot_id, + port_id=port_id)) + + adapter.remove_nio(port_id) + + def slot_enable_nio(self, slot_id, port_id): + """ + Enables a slot NIO binding. + + :param slot_id: slot ID + :param port_id: port ID + """ + + if self.is_running(): # running router + self._hypervisor.send("vm slot_enable_nio {name} {slot_id} {port_id}".format(name=self._name, + slot_id=slot_id, + port_id=port_id)) + + def slot_disable_nio(self, slot_id, port_id): + """ + Disables a slot NIO binding. + + :param slot_id: slot ID + :param port_id: port ID + """ + + if self.is_running(): # running router + self._hypervisor.send("vm slot_disable_nio {name} {slot_id} {port_id}".format(name=self._name, + slot_id=slot_id, + port_id=port_id)) + + def _create_slots(self, numslots): + """ + Creates the appropriate number of slots for this router. + + :param numslots: number of slots to create + """ + + self._slots = numslots * [None] + + @property + def slots(self): + """ + Returns the slots for this router. + + :return: slot list + """ + + return self._slots diff --git a/gns3server/stomp/frame.py b/gns3server/stomp/frame.py deleted file mode 100644 index 90d83e48..00000000 --- a/gns3server/stomp/frame.py +++ /dev/null @@ -1,161 +0,0 @@ -# -*- 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 . - -""" -STOMP frame representation, decoding and encoding -http://stomp.github.io/stomp-specification-1.2.html -Adapted from Jason R. Briggs's code -https://github.com/jasonrbriggs/stomp.py -""" - -import re -from .utils import encode - - -class Frame(object): - """ - A STOMP frame. Comprises a command, the headers and the body content. - """ - - # Used to parse STOMP header lines in the format "key:value", - HEADER_LINE_RE = re.compile('(?P[^:]+)[:](?P.*)') - # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. - PREAMBLE_END_RE = re.compile('\n\n|\r\n\r\n') - # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. - LINE_END_RE = re.compile('\n|\r\n') - # NULL value - NULL = b'\x00' - - def __init__(self, cmd=None, headers={}, body=None): - self._cmd = cmd - self._headers = headers - self._body = body - - @property - def cmd(self): - - return(self._cmd) - - @cmd.setter - def cmd(self, cmd): - - self._cmd = cmd - - @property - def headers(self): - - return(self._headers) - - @headers.setter - def headers(self, headers): - - self._headers = headers - - @property - def body(self): - - return(self._body) - - @body.setter - def body(self, body): - - self._body = body - - def encode(self): - """ - Encodes this frame to be send on the wire - """ - - lines = [] - if self._cmd: - lines.append(self._cmd) - lines.append("\n") - for key, vals in sorted(self._headers.items()): - if type(vals) != tuple: - vals = (vals,) - for val in vals: - lines.append("%s:%s\n" % (key, val)) - lines.append("\n") - if self._body: - lines.append(self._body) - - if self._cmd: - lines.append(self.NULL) - - encoded_lines = (encode(line) for line in lines) - return b''.join(encoded_lines) - - @classmethod - def parse_headers(cls, lines, offset=0): - """ - Parses frame headers - - :param lines: Frame preamble lines - :param offset: To start parsing at the given offset - - :returns: Headers in dict header:value - """ - - headers = {} - for header_line in lines[offset:]: - header_match = cls.HEADER_LINE_RE.match(header_line) - if header_match: - key = header_match.group('key') - if key not in headers: - headers[key] = header_match.group('value') - return headers - - @classmethod - def parse_frame(cls, frame): - """ - Parses a frame - - :params frame: The frame data to be parsed - - :returns: STOMP Frame object - """ - - f = Frame() - # End-of-line (EOL) indicates an heart beat frame - if frame == '\x0a': - f.cmd = 'heartbeat' # This will have the frame ignored - return f - - mat = cls.PREAMBLE_END_RE.search(frame) - preamble_end = -1 - if mat: - preamble_end = mat.start() - if preamble_end == -1: - preamble_end = len(frame) - preamble = frame[0:preamble_end] - preamble_lines = cls.LINE_END_RE.split(preamble) - f.body = frame[preamble_end + 2:] - if f.body[-1] == '\x00': - f.body = f.body[:-1] - - # Skip any leading newlines - first_line = 0 - while first_line < len(preamble_lines) and len(preamble_lines[first_line]) == 0: - first_line += 1 - - # Extract frame type/command - f.cmd = preamble_lines[first_line] - - # Put headers into a key/value map - f.headers = cls.parse_headers(preamble_lines, first_line + 1) - - return f diff --git a/gns3server/stomp/protocol.py b/gns3server/stomp/protocol.py deleted file mode 100644 index 0491c1bf..00000000 --- a/gns3server/stomp/protocol.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- 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 . - -""" -Basic STOMP 1.2 protocol implementation -http://stomp.github.io/stomp-specification-1.2.html -""" - -import uuid -from .frame import Frame -from .utils import encode, hasbyte - -# Commands server-side -CMD_CONNECTED = 'CONNECTED' -CMD_ERROR = 'ERROR' -CMD_MESSAGE = 'MESSAGE' -CMD_RECEIPT = 'RECEIPT' - -# Commands client-side -CMD_STOMP = 'STOMP' -CMD_CONNECT = 'CONNECT' -CMD_DISCONNECT = 'DISCONNECT' -CMD_SEND = 'SEND' - -# Commands not supported -CMD_SUBSCRIBE = 'SUBSCRIBE' -CMD_UNSUBSCRIBE = 'UNSUBSCRIBE' -CMD_ACK = 'ACK' -CMD_NACK = 'NACK' -CMD_BEGIN = 'BEGIN' -CMD_ABORT = 'ABORT' - -# Headers -HDR_VERSION = 'version' -HDR_SESSION = 'session' -HDR_SERVER = 'server' -HDR_CONTENT_TYPE = 'content-type' -HDR_CONTENT_LENGTH = 'content-length' -HDR_RECEIPT_ID = 'receipt-id' -HDR_MESSAGE = 'message' -HDR_MESSAGE_ID = 'message-id' -HDR_ACCEPT_VERSION = 'accept-version' -HDR_HOST = 'host' -HDR_DESTINATION = 'destination' -HDR_RECEIPT = 'receipt' - -# Headers not supported -HDR_HEARTBEAT = 'heart-beat' -HDR_LOGIN = 'login' -HDR_PASSCODE = 'passcode' -HDR_ID = 'id' -HDR_ACK = 'ack' -HDR_SUBSCRIPTION = 'subscription' -HDR_TRANSACTION = 'transaction' - - -class serverProtocol(object): - """ - STOMP 1.2 protocol support for servers. - """ - - def __init__(self): - - # STOMP protocol version - self.version = 1.2 - - def connected(self, session=None, server=None): - """ - Replies to the CONNECT or STOMP command. - Heart-beat header is not supported. - - :param session: A session identifier that uniquely identifies the session. - :param server: A field that contains information about the STOMP server. - - :returns: STOMP Frame object - """ - - # Version header is required - headers = {HDR_VERSION: self.version} - - if session: - headers[HDR_SESSION] = session - - # The server-name field consists of a name token followed by an - # optional version number token. Example: Apache/1.3.9 - if server: - headers[HDR_SERVER] = server - - return Frame(CMD_CONNECTED, headers).encode() - - def message(self, destination, body, content_type=None, message_id=str(uuid.uuid4())): - """ - Sends a message to a STOMP client. - - :param destination: Destination string - :param body: Data to be added in the frame body - :param content_type: MIME type which describes the format of the body - :param message_id: Unique identifier for that message - - :returns: STOMP Frame object - """ - - # Destination and message id headers are required - headers = {HDR_DESTINATION: destination, - HDR_MESSAGE_ID: message_id} - - # Subscription is required but not implemented on this server - headers[HDR_SUBSCRIPTION] = 0 - - if content_type: - headers[HDR_CONTENT_TYPE] = content_type - - body = encode(body) - if HDR_CONTENT_LENGTH not in headers and hasbyte(0, body): - headers[HDR_CONTENT_LENGTH] = len(body) - - return Frame(CMD_MESSAGE, headers, body).encode() - - def receipt(self, receipt_id): - """ - Sends an acknowledgment for client frame that requests a receipt. - - :param receipt_id: Receipt ID to send back to the client - - :returns: STOMP Frame object - """ - - # Receipt ID header is required (the same sent in the client frame) - headers = {HDR_RECEIPT_ID: receipt_id} - return Frame(CMD_RECEIPT, headers).encode() - - def error(self, message='', body='', content_type=None): - """ - Sends an error to the client if something goes wrong. - - :param message: Short description of the error - :param body: Detailed information - :param content_type: MIME type which describes the format of the body - - :returns: STOMP Frame object - """ - - headers = {} - if message: - headers[HDR_MESSAGE] = message - - if body: - body = encode(body) - if HDR_CONTENT_LENGTH not in headers and hasbyte(0, body): - headers[HDR_CONTENT_LENGTH] = len(body) - if content_type: - headers[HDR_CONTENT_TYPE] = content_type - - return Frame(CMD_ERROR, headers, body).encode() - - -class clientProtocol(object): - """ - STOMP 1.2 protocol support for clients. - """ - - def connect(self, host, accept_version='1.2'): - """ - Connects to a STOMP server. - Heart-beat, login and passcode headers are not supported. - - :param host: Host name that the socket was established against. - :param accept_version: The versions of the STOMP protocol the client supports. - - :returns: STOMP Frame object - """ - - # Currently only STOMP 1.2 is supported (required header) - headers = {HDR_ACCEPT_VERSION: accept_version} - - if host: - headers[HDR_HOST] = host - - # The STOMP command is not backward compatible with STOMP 1.0 servers. - # Clients that use the STOMP frame instead of the CONNECT frame will - # only be able to connect to STOMP 1.2 servers (as well as some STOMP 1.1 servers. - return Frame(CMD_STOMP, headers).encode() - - def disconnect(self, receipt=str(uuid.uuid4())): - """ - Disconnects to a STOMP server. - - :param receipt: unique identifier - - :returns: STOMP Frame object - """ - - # Receipt header is required - headers = {HDR_RECEIPT: receipt} - return Frame(CMD_DISCONNECT, headers).encode() - - def send(self, destination, body, content_type=None): - """ - Sends a message to a destination in the messaging system. - Transaction header is not supported. - User defined headers are not supported too (against the protocol specification) - - :param destination: Destination string - :param body: Data to be added in the frame body - :param content_type: MIME type which describes the format of the body - - :returns: STOMP Frame object - """ - - # Destination header is required - headers = {HDR_DESTINATION: destination} - - if content_type: - headers[HDR_CONTENT_TYPE] = content_type - - body = encode(body) - if HDR_CONTENT_LENGTH not in headers and hasbyte(0, body): - headers[HDR_CONTENT_LENGTH] = len(body) - - return Frame(CMD_SEND, headers, body).encode() diff --git a/gns3server/topology.py b/gns3server/topology.py new file mode 100644 index 00000000..e14ce555 --- /dev/null +++ b/gns3server/topology.py @@ -0,0 +1,49 @@ +# -*- 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 . + +#import networkx as nx + + +class Topology(object): + + def __init__(self): + + pass + #self._topology = nx.Graph() + + def add_node(self, node): + + self._topology.add_node(node) + + def remove_node(self, node): + + self._topology.remove_node(node) + + def clear(self): + + self._topology.clear() + + def __str__(self): + + return "GNS3 network topology" + + @staticmethod + def instance(): + + if not hasattr(Topology, "_instance"): + Topology._instance = Topology() + return Topology._instance diff --git a/tests/dynamips/.gitignore b/tests/dynamips/.gitignore new file mode 100644 index 00000000..39ffa4b5 --- /dev/null +++ b/tests/dynamips/.gitignore @@ -0,0 +1 @@ +/c3725.image diff --git a/tests/dynamips/conftest.py b/tests/dynamips/conftest.py new file mode 100644 index 00000000..9d7f294a --- /dev/null +++ b/tests/dynamips/conftest.py @@ -0,0 +1,30 @@ +from gns3server.modules.dynamips import HypervisorManager +import pytest +import os + + +@pytest.fixture(scope="module") +def hypervisor(request): + + cwd = os.path.dirname(os.path.abspath(__file__)) + dynamips_path = os.path.join(cwd, "dynamips.stable") + print("\nStarting Dynamips Hypervisor: {}".format(dynamips_path)) + manager = HypervisorManager(dynamips_path, "/tmp", base_port=9000) + hypervisor = manager.start_new_hypervisor() + + def stop(): + print("\nStopping Dynamips Hypervisor") + manager.stop_all_hypervisors() + + request.addfinalizer(stop) + return hypervisor + + +@pytest.fixture(scope="session") +def image(request): + + cwd = os.path.dirname(os.path.abspath(__file__)) + image_path = os.path.join(cwd, "c3725.image") + if not os.path.exists(image_path): + return None + return image_path diff --git a/tests/dynamips/dynamips.stable b/tests/dynamips/dynamips.stable new file mode 100755 index 00000000..df7950ca Binary files /dev/null and b/tests/dynamips/dynamips.stable differ diff --git a/tests/dynamips/test_atm_bridge.py b/tests/dynamips/test_atm_bridge.py new file mode 100644 index 00000000..8b84fe61 --- /dev/null +++ b/tests/dynamips/test_atm_bridge.py @@ -0,0 +1,62 @@ +from gns3server.modules.dynamips import ATMBridge +from gns3server.modules.dynamips import NIO_Null +from gns3server.modules.dynamips import DynamipsError +import pytest + + +@pytest.fixture +def atm_bridge(request, hypervisor): + + atm_bridge = ATMBridge(hypervisor, "ATM bridge") + request.addfinalizer(atm_bridge.delete) + return atm_bridge + + +def test_atm_bridge_exists(atm_bridge): + + assert atm_bridge.list() + + +def test_rename_atm_bridge(atm_bridge): + + atm_bridge.rename("new ATM bridge") + assert atm_bridge.name == "new ATM bridge" + + +def test_add_remove_nio(atm_bridge): + + nio = NIO_Null(atm_bridge.hypervisor) + atm_bridge.add_nio(nio, 0) # add NIO on port 0 + assert atm_bridge.nios + atm_bridge.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_add_nio_already_allocated_port(atm_bridge): + + nio = NIO_Null(atm_bridge.hypervisor) + atm_bridge.add_nio(nio, 0) # add NIO on port 0 + with pytest.raises(DynamipsError): + atm_bridge.add_nio(nio, 0) + nio.delete() + + +def test_remove_nio_non_allocated_port(atm_bridge): + + with pytest.raises(DynamipsError): + atm_bridge.remove_nio(0) # remove NIO from port 0 + + +def test_bridge(atm_bridge): + + nio1 = NIO_Null(atm_bridge.hypervisor) + atm_bridge.add_nio(nio1, 0) # add NIO on port 0 (Ethernet NIO) + nio2 = NIO_Null(atm_bridge.hypervisor) + atm_bridge.add_nio(nio1, 1) # add NIO on port 1 (ATM NIO) + atm_bridge.configure(0, 1, 10, 10) # configure Ethernet port 0 -> ATM port 1 with VC 10:10 + assert atm_bridge.mapping[0] == (1, 10, 10) + atm_bridge.unconfigure() + atm_bridge.remove_nio(0) + atm_bridge.remove_nio(1) + nio1.delete() + nio2.delete() diff --git a/tests/dynamips/test_atm_switch.py b/tests/dynamips/test_atm_switch.py new file mode 100644 index 00000000..8d2f92f6 --- /dev/null +++ b/tests/dynamips/test_atm_switch.py @@ -0,0 +1,83 @@ +from gns3server.modules.dynamips import ATMSwitch +from gns3server.modules.dynamips import NIO_Null +from gns3server.modules.dynamips import DynamipsError +import pytest + + +@pytest.fixture +def atmsw(request, hypervisor): + + atmsw = ATMSwitch(hypervisor, "ATM switch") + request.addfinalizer(atmsw.delete) + return atmsw + + +def test_atmsw_exists(atmsw): + + assert atmsw.list() + + +def test_rename_atmsw(atmsw): + + atmsw.rename("new ATM switch") + assert atmsw.name == "new ATM switch" + + +def test_add_remove_nio(atmsw): + + nio = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio, 0) # add NIO on port 0 + assert atmsw.nios + atmsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_add_nio_already_allocated_port(atmsw): + + nio = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio, 0) # add NIO on port 0 + with pytest.raises(DynamipsError): + atmsw.add_nio(nio, 0) + nio.delete() + + +def test_remove_nio_non_allocated_port(atmsw): + + with pytest.raises(DynamipsError): + atmsw.remove_nio(0) # remove NIO from port 0 + + +def test_vp(atmsw): + + nio1 = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio1, 0) # add NIO on port 0 + nio2 = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio1, 1) # add NIO on port 1 + atmsw.map_vp(0, 10, 1, 20) # port 0 VP 10 to port 1 VP 20 (unidirectional) + atmsw.map_vp(1, 20, 0, 10) # port 1 VP 20 to port 0 VP 10 (unidirectional) + assert atmsw.mapping[(0, 10)] == (1, 20) + assert atmsw.mapping[(1, 20)] == (0, 10) + atmsw.unmap_vp(0, 10, 1, 20) # port 0 VP 10 to port 1 VP 20 (unidirectional) + atmsw.unmap_vp(1, 20, 0, 10) # port 1 VP 20 to port 0 VP 10 (unidirectional) + atmsw.remove_nio(0) + atmsw.remove_nio(1) + nio1.delete() + nio2.delete() + + +def test_pvc(atmsw): + + nio1 = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio1, 0) # add NIO on port 0 + nio2 = NIO_Null(atmsw.hypervisor) + atmsw.add_nio(nio1, 1) # add NIO on port 1 + atmsw.map_pvc(0, 10, 10, 1, 20, 20) # port 0 VC 10:10 to port 1 VP 20:20 (unidirectional) + atmsw.map_pvc(1, 20, 20, 0, 10, 10) # port 1 VC 20:20 to port 0 VC 10:10 (unidirectional) + assert atmsw.mapping[(0, 10, 10)] == (1, 20, 20) + assert atmsw.mapping[(1, 20, 20)] == (0, 10, 10) + atmsw.unmap_pvc(0, 10, 10, 1, 20, 20) # port 0 VC 10:10 to port 1 VP 20:20 (unidirectional) + atmsw.unmap_pvc(1, 20, 20, 0, 10, 10) # port 1 VC 20:20 to port 0 VC 10:10 (unidirectional) + atmsw.remove_nio(0) + atmsw.remove_nio(1) + nio1.delete() + nio2.delete() diff --git a/tests/dynamips/test_bridge.py b/tests/dynamips/test_bridge.py new file mode 100644 index 00000000..6ab07ee1 --- /dev/null +++ b/tests/dynamips/test_bridge.py @@ -0,0 +1,31 @@ +from gns3server.modules.dynamips import Bridge +from gns3server.modules.dynamips import NIO_Null +import pytest + + +@pytest.fixture +def bridge(request, hypervisor): + + bridge = Bridge(hypervisor, "bridge") + request.addfinalizer(bridge.delete) + return bridge + + +def test_bridge_exists(bridge): + + assert bridge.list() + + +def test_rename_bridge(bridge): + + bridge.rename("new bridge") + assert bridge.name == "new bridge" + + +def test_add_remove_nio(bridge): + + nio = NIO_Null(bridge.hypervisor) + bridge.add_nio(nio) + assert bridge.nios + bridge.remove_nio(nio) + nio.delete() diff --git a/tests/dynamips/test_c1700.py b/tests/dynamips/test_c1700.py new file mode 100644 index 00000000..2202a50f --- /dev/null +++ b/tests/dynamips/test_c1700.py @@ -0,0 +1,167 @@ +from gns3server.modules.dynamips import C1700 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import WIC_2T +from gns3server.modules.dynamips import WIC_1ENET +from gns3server.modules.dynamips import NIO_Null +import pytest + + +@pytest.fixture +def router_c1700(request, hypervisor): + + router = C1700(hypervisor, "c1700 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c1700): + + assert router_c1700.platform == "c1700" + assert router_c1700.list() + + +def test_chassis_1721(hypervisor): + + router = C1700(hypervisor, "1721 chassis", chassis="1721") + assert router.chassis == "1721" + assert str(router.slots[0]) == "C1700-MB-1FE" + router.delete() + + +def test_chassis_change_to_1721(router_c1700): + + assert router_c1700.chassis == "1720" # default chassis + router_c1700.chassis = "1721" + assert router_c1700.chassis == "1721" + + +def test_chassis_1750(hypervisor): + + router = C1700(hypervisor, "1750 chassis", chassis="1750") + assert router.chassis == "1750" + assert str(router.slots[0]) == "C1700-MB-1FE" + router.delete() + + +def test_chassis_change_to_1750(router_c1700): + + assert router_c1700.chassis == "1720" # default chassis + router_c1700.chassis = "1750" + assert router_c1700.chassis == "1750" + + +def test_chassis_1751(hypervisor): + + router = C1700(hypervisor, "1751 chassis", chassis="1751") + assert router.chassis == "1751" + assert str(router.slots[0]) == "C1700-MB-1FE" + router.delete() + + +def test_chassis_change_to_1751(router_c1700): + + assert router_c1700.chassis == "1720" # default chassis + router_c1700.chassis = "1751" + assert router_c1700.chassis == "1751" + + +def test_chassis_1760(hypervisor): + + router = C1700(hypervisor, "1760 chassis", chassis="1760") + assert router.chassis == "1760" + assert str(router.slots[0]) == "C1700-MB-1FE" + router.delete() + + +def test_chassis_change_to_1760(router_c1700): + + assert router_c1700.chassis == "1720" # default chassis + router_c1700.chassis = "1760" + assert router_c1700.chassis == "1760" + + +def test_iomem(router_c1700): + + assert router_c1700.iomem == 15 # default value + router_c1700.iomem = 20 + assert router_c1700.iomem == 20 + + +def test_mac_addr(router_c1700): + + assert router_c1700.mac_addr == None # default value + router_c1700.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c1700.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c1700): + + with pytest.raises(DynamipsError): + router_c1700.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c1700): + + assert router_c1700.system_id == None # default value + router_c1700.system_id = "FTX0945W0MO" + assert router_c1700.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c1700): + + router_c1700.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_install_remove_wic(router_c1700): + + wic = WIC_2T() + router_c1700.install_wic(0, wic) # install in WIC slot 0 + assert router_c1700.slots[0].wics[0] + wic = WIC_1ENET() + router_c1700.install_wic(1, wic) # install in WIC slot 1 + assert router_c1700.slots[0].wics[1] + router_c1700.uninstall_wic(0) # uninstall WIC from slot 0 + assert not router_c1700.slots[0].wics[0] + + +def test_install_wic_into_wrong_slot(router_c1700): + + wic = WIC_2T() + with pytest.raises(DynamipsError): + router_c1700.install_wic(2, wic) # install in WIC slot 2 + + +def test_install_wic_into_already_occupied_slot(router_c1700): + + wic = WIC_2T() + router_c1700.install_wic(0, wic) # install in WIC slot 0 + wic = WIC_1ENET() + with pytest.raises(DynamipsError): + router_c1700.install_wic(0, wic) # install in WIC slot 0 + + +def test_wic_add_remove_nio_binding(router_c1700): + + nio = NIO_Null(router_c1700.hypervisor) + wic = WIC_2T() + router_c1700.install_wic(0, wic) # install WIC in slot 0 + router_c1700.slot_add_nio_binding(0, 17, nio) # slot 0/17 (slot 0, wic 0, port 1) + assert router_c1700.slots[0].ports[17] == nio + assert router_c1700.get_slot_nio_bindings(slot_id=0) + router_c1700.slot_remove_nio_binding(0, 17) # slot 0/17 (slot 0, wic 0, port 1) + assert not router_c1700.get_slot_nio_bindings(slot_id=0) + assert not router_c1700.slots[0].ports[17] == nio + nio.delete() + + +def test_wic_add_remove_nio_binding_for_chassis_1760(hypervisor): + + router = C1700(hypervisor, "1760 chassis", chassis="1760") + nio = NIO_Null(router.hypervisor) + wic = WIC_2T() + router.install_wic(1, wic) # install WIC in slot 1 + router.slot_add_nio_binding(0, 32, nio) # slot 0/17 (slot 0, wic 1, port 0) + router.slot_remove_nio_binding(0, 32) + assert not router.get_slot_nio_bindings(slot_id=0) + nio.delete() + router.delete() diff --git a/tests/dynamips/test_c2600.py b/tests/dynamips/test_c2600.py new file mode 100644 index 00000000..d17ee345 --- /dev/null +++ b/tests/dynamips/test_c2600.py @@ -0,0 +1,216 @@ +from gns3server.modules.dynamips import C2600 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import NM_1E +from gns3server.modules.dynamips import NM_4E +from gns3server.modules.dynamips import NM_1FE_TX +from gns3server.modules.dynamips import NM_16ESW +import pytest + + +@pytest.fixture +def router_c2600(request, hypervisor): + + router = C2600(hypervisor, "c2600 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c2600): + + assert router_c2600.platform == "c2600" + assert router_c2600.list() + + +def test_chassis_2611(hypervisor): + + router = C2600(hypervisor, "2611 chassis", chassis="2611") + assert router.chassis == "2611" + assert isinstance(router.slots[0], router.integrated_adapters["2611"]) + router.delete() + + +def test_chassis_change_to_2611(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2611" + assert router_c2600.chassis == "2611" + + +def test_chassis_2620(hypervisor): + + router = C2600(hypervisor, "2620 chassis", chassis="2620") + assert router.chassis == "2620" + assert isinstance(router.slots[0], router.integrated_adapters["2620"]) + router.delete() + + +def test_chassis_change_to_2620(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2620" + assert router_c2600.chassis == "2620" + + +def test_chassis_2621(hypervisor): + + router = C2600(hypervisor, "2621 chassis", chassis="2621") + assert router.chassis == "2621" + assert isinstance(router.slots[0], router.integrated_adapters["2621"]) + router.delete() + + +def test_chassis_change_to_2621(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2621" + assert router_c2600.chassis == "2621" + + +def test_chassis_2610XM(hypervisor): + + router = C2600(hypervisor, "2610XM chassis", chassis="2610XM") + assert router.chassis == "2610XM" + assert isinstance(router.slots[0], router.integrated_adapters["2610XM"]) + router.delete() + + +def test_chassis_change_to_2610XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2610XM" + assert router_c2600.chassis == "2610XM" + + +def test_chassis_2611XM(hypervisor): + + router = C2600(hypervisor, "2611XM chassis", chassis="2611XM") + assert router.chassis == "2611XM" + assert isinstance(router.slots[0], router.integrated_adapters["2611XM"]) + router.delete() + + +def test_chassis_change_to_2611XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2611XM" + assert router_c2600.chassis == "2611XM" + + +def test_chassis_2620XM(hypervisor): + + router = C2600(hypervisor, "2620XM chassis", chassis="2620XM") + assert router.chassis == "2620XM" + assert isinstance(router.slots[0], router.integrated_adapters["2620XM"]) + router.delete() + + +def test_chassis_change_to_2620XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2620XM" + assert router_c2600.chassis == "2620XM" + + +def test_chassis_2621XM(hypervisor): + + router = C2600(hypervisor, "2621XM chassis", chassis="2621XM") + assert router.chassis == "2621XM" + assert isinstance(router.slots[0], router.integrated_adapters["2621XM"]) + router.delete() + + +def test_chassis_change_to_2621XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2621XM" + assert router_c2600.chassis == "2621XM" + + +def test_chassis_2650XM(hypervisor): + + router = C2600(hypervisor, "2650XM chassis", chassis="2650XM") + assert router.chassis == "2650XM" + assert isinstance(router.slots[0], router.integrated_adapters["2650XM"]) + router.delete() + + +def test_chassis_change_to_2650XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2650XM" + assert router_c2600.chassis == "2650XM" + + +def test_chassis_2651XM(hypervisor): + + router = C2600(hypervisor, "2651XM chassis", chassis="2651XM") + assert router.chassis == "2651XM" + assert isinstance(router.slots[0], router.integrated_adapters["2651XM"]) + router.delete() + + +def test_chassis_change_to_2651XM(router_c2600): + + assert router_c2600.chassis == "2610" # default chassis + router_c2600.chassis = "2651XM" + assert router_c2600.chassis == "2651XM" + + +def test_iomem(router_c2600): + + assert router_c2600.iomem == 15 # default value + router_c2600.iomem = 20 + assert router_c2600.iomem == 20 + + +def test_mac_addr(router_c2600): + + assert router_c2600.mac_addr == None # default value + router_c2600.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c2600.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c2600): + + with pytest.raises(DynamipsError): + router_c2600.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c2600): + + assert router_c2600.system_id == None # default value + router_c2600.system_id = "FTX0945W0MO" + assert router_c2600.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c2600): + + router_c2600.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_NM_1E(router_c2600): + + adapter = NM_1E() + router_c2600.slot_add_binding(1, adapter) + assert router_c2600.slots[1] == adapter + + +def test_slot_add_NM_4E(router_c2600): + + adapter = NM_4E() + router_c2600.slot_add_binding(1, adapter) + assert router_c2600.slots[1] == adapter + + +def test_slot_add_NM_1FE_TX(router_c2600): + + adapter = NM_1FE_TX() + router_c2600.slot_add_binding(1, adapter) + assert router_c2600.slots[1] == adapter + + +def test_slot_add_NM_16ESW(router_c2600): + + adapter = NM_16ESW() + router_c2600.slot_add_binding(1, adapter) + assert router_c2600.slots[1] == adapter diff --git a/tests/dynamips/test_c2691.py b/tests/dynamips/test_c2691.py new file mode 100644 index 00000000..2af7e94a --- /dev/null +++ b/tests/dynamips/test_c2691.py @@ -0,0 +1,75 @@ +from gns3server.modules.dynamips import C2691 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import NM_1FE_TX +from gns3server.modules.dynamips import NM_4T +from gns3server.modules.dynamips import NM_16ESW +import pytest + + +@pytest.fixture +def router_c2691(request, hypervisor): + + router = C2691(hypervisor, "c2691 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c2691): + + assert router_c2691.platform == "c2691" + assert router_c2691.list() + + +def test_iomem(router_c2691): + + assert router_c2691.iomem == 5 # default value + router_c2691.iomem = 10 + assert router_c2691.iomem == 10 + + +def test_mac_addr(router_c2691): + + assert router_c2691.mac_addr == None # default value + router_c2691.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c2691.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c2691): + + with pytest.raises(DynamipsError): + router_c2691.mac_addr = "zz:zz:zz:zz:zz:zz" + + +# FIXME: no implemented within Dynamips +# def test_system_id(hypervisor): +# router = C2691(hypervisor, "test system id") +# assert router.system_id == None # default value +# router.system_id = "FTX0945W0MO" +# assert router.system_id == "FTX0945W0MO" +# router.delete() + + +def test_get_hardware_info(router_c2691): + + router_c2691.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_NM_1FE_TX(router_c2691): + + adapter = NM_1FE_TX() + router_c2691.slot_add_binding(1, adapter) + assert router_c2691.slots[1] == adapter + + +def test_slot_add_NM_4T(router_c2691): + + adapter = NM_4T() + router_c2691.slot_add_binding(1, adapter) + assert router_c2691.slots[1] == adapter + + +def test_slot_add_NM_16ESW(router_c2691): + + adapter = NM_16ESW() + router_c2691.slot_add_binding(1, adapter) + assert router_c2691.slots[1] == adapter diff --git a/tests/dynamips/test_c3600.py b/tests/dynamips/test_c3600.py new file mode 100644 index 00000000..f3bd06cb --- /dev/null +++ b/tests/dynamips/test_c3600.py @@ -0,0 +1,118 @@ +from gns3server.modules.dynamips import C3600 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import NM_1E +from gns3server.modules.dynamips import NM_4E +from gns3server.modules.dynamips import NM_1FE_TX +from gns3server.modules.dynamips import NM_16ESW +from gns3server.modules.dynamips import NM_4T +import pytest + + +@pytest.fixture +def router_c3600(request, hypervisor): + + router = C3600(hypervisor, "c3600 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exist(router_c3600): + + assert router_c3600.platform == "c3600" + assert router_c3600.list() + + +def test_chassis_3620(hypervisor): + + router = C3600(hypervisor, "3620 chassis", chassis="3620") + assert router.chassis == "3620" + router.delete() + + +def test_chassis_change_to_3620(router_c3600): + + assert router_c3600.chassis == "3640" # default chassis + router_c3600.chassis = "3620" + assert router_c3600.chassis == "3620" + + +def test_chassis_3660(hypervisor): + + router = C3600(hypervisor, "3660 chassis", chassis="3660") + assert router.chassis == "3660" + assert str(router.slots[0]) == "Leopard-2FE" + router.delete() + + +def test_chassis_change_to_3660(router_c3600): + + assert router_c3600.chassis == "3640" # default chassis + router_c3600.chassis = "3660" + assert router_c3600.chassis == "3660" + + +def test_iomem(router_c3600): + + assert router_c3600.iomem == 5 # default value + router_c3600.iomem = 10 + assert router_c3600.iomem == 10 + + +def test_mac_addr(router_c3600): + + assert router_c3600.mac_addr == None # default value + router_c3600.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c3600.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c3600): + + with pytest.raises(DynamipsError): + router_c3600.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c3600): + + assert router_c3600.system_id == None # default value + router_c3600.system_id = "FTX0945W0MO" + assert router_c3600.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c3600): + + router_c3600.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_NM_1E(router_c3600): + + adapter = NM_1E() + router_c3600.slot_add_binding(1, adapter) + assert router_c3600.slots[1] == adapter + + +def test_slot_add_NM_4E(router_c3600): + + adapter = NM_4E() + router_c3600.slot_add_binding(1, adapter) + assert router_c3600.slots[1] == adapter + + +def test_slot_add_NM_1FE_TX(router_c3600): + + adapter = NM_1FE_TX() + router_c3600.slot_add_binding(1, adapter) + assert router_c3600.slots[1] == adapter + + +def test_slot_add_NM_16ESW(router_c3600): + + adapter = NM_16ESW() + router_c3600.slot_add_binding(1, adapter) + assert router_c3600.slots[1] == adapter + + +def test_slot_add_NM_4T(router_c3600): + + adapter = NM_4T() + router_c3600.slot_add_binding(1, adapter) + assert router_c3600.slots[1] == adapter diff --git a/tests/dynamips/test_c3725.py b/tests/dynamips/test_c3725.py new file mode 100644 index 00000000..5bb6f6e9 --- /dev/null +++ b/tests/dynamips/test_c3725.py @@ -0,0 +1,73 @@ +from gns3server.modules.dynamips import C3725 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import NM_1FE_TX +from gns3server.modules.dynamips import NM_4T +from gns3server.modules.dynamips import NM_16ESW +import pytest + + +@pytest.fixture +def router_c3725(request, hypervisor): + + router = C3725(hypervisor, "c3725 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c3725): + + assert router_c3725.platform == "c3725" + assert router_c3725.list() + + +def test_iomem(router_c3725): + + assert router_c3725.iomem == 5 # default value + router_c3725.iomem = 10 + assert router_c3725.iomem == 10 + + +def test_mac_addr(router_c3725): + + assert router_c3725.mac_addr == None # default value + router_c3725.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c3725.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c3725): + + with pytest.raises(DynamipsError): + router_c3725.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c3725): + + assert router_c3725.system_id == None # default value + router_c3725.system_id = "FTX0945W0MO" + assert router_c3725.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c3725): + + router_c3725.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_NM_1FE_TX(router_c3725): + + adapter = NM_1FE_TX() + router_c3725.slot_add_binding(1, adapter) + assert router_c3725.slots[1] == adapter + + +def test_slot_add_NM_4T(router_c3725): + + adapter = NM_4T() + router_c3725.slot_add_binding(1, adapter) + assert router_c3725.slots[1] == adapter + + +def test_slot_add_NM_16ESW(router_c3725): + + adapter = NM_16ESW() + router_c3725.slot_add_binding(1, adapter) + assert router_c3725.slots[1] == adapter diff --git a/tests/dynamips/test_c3745.py b/tests/dynamips/test_c3745.py new file mode 100644 index 00000000..78cc53e3 --- /dev/null +++ b/tests/dynamips/test_c3745.py @@ -0,0 +1,73 @@ +from gns3server.modules.dynamips import C3745 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import NM_1FE_TX +from gns3server.modules.dynamips import NM_4T +from gns3server.modules.dynamips import NM_16ESW +import pytest + + +@pytest.fixture +def router_c3745(request, hypervisor): + + router = C3745(hypervisor, "c3745 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c3745): + + assert router_c3745.platform == "c3745" + assert router_c3745.list() + + +def test_iomem(router_c3745): + + assert router_c3745.iomem == 5 # default value + router_c3745.iomem = 10 + assert router_c3745.iomem == 10 + + +def test_mac_addr(router_c3745): + + assert router_c3745.mac_addr == None # default value + router_c3745.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c3745.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c3745): + + with pytest.raises(DynamipsError): + router_c3745.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c3745): + + assert router_c3745.system_id == None # default value + router_c3745.system_id = "FTX0945W0MO" + assert router_c3745.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c3745): + + router_c3745.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_NM_1FE_TX(router_c3745): + + adapter = NM_1FE_TX() + router_c3745.slot_add_binding(1, adapter) + assert router_c3745.slots[1] == adapter + + +def test_slot_add_NM_4T(router_c3745): + + adapter = NM_4T() + router_c3745.slot_add_binding(1, adapter) + assert router_c3745.slots[1] == adapter + + +def test_slot_add_NM_16ESW(router_c3745): + + adapter = NM_16ESW() + router_c3745.slot_add_binding(1, adapter) + assert router_c3745.slots[1] == adapter diff --git a/tests/dynamips/test_c7200.py b/tests/dynamips/test_c7200.py new file mode 100644 index 00000000..d3929188 --- /dev/null +++ b/tests/dynamips/test_c7200.py @@ -0,0 +1,188 @@ +from gns3server.modules.dynamips import C7200 +from gns3server.modules.dynamips import DynamipsError +from gns3server.modules.dynamips import PA_2FE_TX +from gns3server.modules.dynamips import PA_4E +from gns3server.modules.dynamips import PA_4T +from gns3server.modules.dynamips import PA_8E +from gns3server.modules.dynamips import PA_8T +from gns3server.modules.dynamips import PA_A1 +from gns3server.modules.dynamips import PA_FE_TX +from gns3server.modules.dynamips import PA_GE +from gns3server.modules.dynamips import PA_POS_OC3 +from gns3server.modules.dynamips import NIO_Null +import pytest + + +@pytest.fixture +def router_c7200(request, hypervisor): + + router = C7200(hypervisor, "c7200 router") + request.addfinalizer(router.delete) + return router + + +def test_router_exists(router_c7200): + + assert router_c7200.platform == "c7200" + assert router_c7200.list() + + +def test_npe(router_c7200): + + assert router_c7200.npe == "npe-400" # default value + router_c7200.npe = "npe-200" + assert router_c7200.npe == "npe-200" + + +def test_midplane(router_c7200): + + assert router_c7200.midplane == "vxr" # default value + router_c7200.midplane = "std" + assert router_c7200.midplane == "std" + + +def test_sensors(router_c7200): + + assert router_c7200.sensors == [22, 22, 22, 22] # default values (everything at 22C) + router_c7200.sensors = [25, 25, 25, 25] + assert router_c7200.sensors == [25, 25, 25, 25] + + +def test_power_supplies(router_c7200): + + assert router_c7200.power_supplies == [1, 1] # default values (1 = powered on) + router_c7200.power_supplies = [0, 0] + assert router_c7200.power_supplies == [0, 0] + + +def test_mac_addr(router_c7200): + + assert router_c7200.mac_addr == None # default value + router_c7200.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router_c7200.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router_c7200): + + with pytest.raises(DynamipsError): + router_c7200.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router_c7200): + + assert router_c7200.system_id == None # default value + router_c7200.system_id = "FTX0945W0MO" + assert router_c7200.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router_c7200): + + router_c7200.get_hardware_info() # FIXME: Dynamips doesn't return anything + + +def test_slot_add_PA_2FE_TX(router_c7200): + + adapter = PA_2FE_TX() + router_c7200.slot_add_binding(1, adapter) + assert router_c7200.slots[1] == adapter + + +def test_slot_add_PA_4E(router_c7200): + + adapter = PA_4E() + router_c7200.slot_add_binding(2, adapter) + assert router_c7200.slots[2] == adapter + + +def test_slot_add_PA_4T(router_c7200): + + adapter = PA_4T() + router_c7200.slot_add_binding(3, adapter) + assert router_c7200.slots[3] == adapter + + +def test_slot_add_PA_8E(router_c7200): + + adapter = PA_8E() + router_c7200.slot_add_binding(4, adapter) + assert router_c7200.slots[4] == adapter + + +def test_slot_add_PA_8T(router_c7200): + + adapter = PA_8T() + router_c7200.slot_add_binding(5, adapter) + assert router_c7200.slots[5] == adapter + + +def test_slot_add_PA_A1(router_c7200): + + adapter = PA_A1() + router_c7200.slot_add_binding(1, adapter) + assert router_c7200.slots[1] == adapter + + +def test_slot_add_PA_FE_TX(router_c7200): + + adapter = PA_FE_TX() + router_c7200.slot_add_binding(2, adapter) + assert router_c7200.slots[2] == adapter + + +def test_slot_add_PA_GE(router_c7200): + + adapter = PA_GE() + router_c7200.slot_add_binding(3, adapter) + assert router_c7200.slots[3] == adapter + + +def test_slot_add_PA_POS_OC3(router_c7200): + + adapter = PA_POS_OC3() + router_c7200.slot_add_binding(4, adapter) + assert router_c7200.slots[4] == adapter + + +def test_slot_add_into_already_occupied_slot(router_c7200): + + adapter = PA_FE_TX() + with pytest.raises(DynamipsError): + router_c7200.slot_add_binding(0, adapter) + + +def test_slot_add_into_wrong_slot(router_c7200): + + adapter = PA_FE_TX() + with pytest.raises(DynamipsError): + router_c7200.slot_add_binding(10, adapter) + + +def test_slot_remove_adapter(router_c7200): + + adapter = PA_FE_TX() + router_c7200.slot_add_binding(1, adapter) + router_c7200.slot_remove_binding(1) + assert router_c7200.slots[1] == None + + +def test_slot_add_remove_nio_binding(router_c7200): + + adapter = PA_FE_TX() + router_c7200.slot_add_binding(1, adapter) + nio = NIO_Null(router_c7200.hypervisor) + router_c7200.slot_add_nio_binding(1, 0, nio) # slot 1/0 + assert router_c7200.get_slot_nio_bindings(slot_id=1) + assert router_c7200.slots[1].ports[0] == nio + router_c7200.slot_remove_nio_binding(1, 0) # slot 1/0 + assert not router_c7200.get_slot_nio_bindings(slot_id=0) + nio.delete() + + +def test_slot_add_nio_to_wrong_port(router_c7200): + + adapter = PA_FE_TX() + router_c7200.slot_add_binding(1, adapter) + nio = NIO_Null(router_c7200.hypervisor) + with pytest.raises(DynamipsError): + router_c7200.slot_add_nio_binding(1, 1, nio) # slot 1/1 + nio.delete() diff --git a/tests/dynamips/test_ethernet_switch.py b/tests/dynamips/test_ethernet_switch.py new file mode 100644 index 00000000..52574ec5 --- /dev/null +++ b/tests/dynamips/test_ethernet_switch.py @@ -0,0 +1,87 @@ +from gns3server.modules.dynamips import EthernetSwitch +from gns3server.modules.dynamips import NIO_Null +from gns3server.modules.dynamips import DynamipsError +import pytest + + +@pytest.fixture +def ethsw(request, hypervisor): + + ethsw = EthernetSwitch(hypervisor, "Ethernet switch") + request.addfinalizer(ethsw.delete) + return ethsw + + +def test_ethsw_exists(ethsw): + + assert ethsw.list() + + +def test_rename_ethsw(ethsw): + + ethsw.rename("new Ethernet switch") + assert ethsw.name == "new Ethernet switch" + + +def test_add_remove_nio(ethsw): + + nio = NIO_Null(ethsw.hypervisor) + ethsw.add_nio(nio, 0) # add NIO on port 0 + assert ethsw.nios + ethsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_add_nio_already_allocated_port(ethsw): + + nio = NIO_Null(ethsw.hypervisor) + ethsw.add_nio(nio, 0) # add NIO on port 0 + with pytest.raises(DynamipsError): + ethsw.add_nio(nio, 0) + nio.delete() + + +def test_remove_nio_non_allocated_port(ethsw): + + with pytest.raises(DynamipsError): + ethsw.remove_nio(0) # remove NIO from port 0 + + +def test_set_access_port(ethsw): + + nio = NIO_Null(ethsw.hypervisor) + ethsw.add_nio(nio, 0) # add NIO on port 0 + ethsw.set_access_port(0, 10) # set port 0 as access in VLAN 10 + assert ethsw.mapping[0] == ("access", 10) + ethsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_set_dot1q_port(ethsw): + + nio = NIO_Null(ethsw.hypervisor) + ethsw.add_nio(nio, 0) # add NIO on port 0 + ethsw.set_dot1q_port(0, 1) # set port 0 as 802.1Q trunk with native VLAN 1 + assert ethsw.mapping[0] == ("dot1q", 1) + ethsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_set_qinq_port(ethsw): + + nio = NIO_Null(ethsw.hypervisor) + ethsw.add_nio(nio, 0) # add NIO on port 0 + ethsw.set_qinq_port(0, 100) # set port 0 as QinQ trunk with outer VLAN 100 + assert ethsw.mapping[0] == ("qinq", 100) + ethsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_get_mac_addr_table(ethsw): + + assert not ethsw.get_mac_addr_table() # MAC address table should be empty + + +def test_clear_mac_addr_table(ethsw): + + ethsw.clear_mac_addr_table() diff --git a/tests/dynamips/test_frame_relay_switch.py b/tests/dynamips/test_frame_relay_switch.py new file mode 100644 index 00000000..79be3d7a --- /dev/null +++ b/tests/dynamips/test_frame_relay_switch.py @@ -0,0 +1,65 @@ +from gns3server.modules.dynamips import FrameRelaySwitch +from gns3server.modules.dynamips import NIO_Null +from gns3server.modules.dynamips import DynamipsError +import pytest + + +@pytest.fixture +def frsw(request, hypervisor): + + frsw = FrameRelaySwitch(hypervisor, "Frane Relay switch") + request.addfinalizer(frsw.delete) + return frsw + + +def test_frsw_exists(frsw): + + assert frsw.list() + + +def test_rename_frsw(frsw): + + frsw.rename("new Frame Relay switch") + assert frsw.name == "new Frame Relay switch" + + +def test_add_remove_nio(frsw): + + nio = NIO_Null(frsw.hypervisor) + frsw.add_nio(nio, 0) # add NIO on port 0 + assert frsw.nios + frsw.remove_nio(0) # remove NIO from port 0 + nio.delete() + + +def test_add_nio_already_allocated_port(frsw): + + nio = NIO_Null(frsw.hypervisor) + frsw.add_nio(nio, 0) # add NIO on port 0 + with pytest.raises(DynamipsError): + frsw.add_nio(nio, 0) + nio.delete() + + +def test_remove_nio_non_allocated_port(frsw): + + with pytest.raises(DynamipsError): + frsw.remove_nio(0) # remove NIO from port 0 + + +def test_vc(frsw): + + nio1 = NIO_Null(frsw.hypervisor) + frsw.add_nio(nio1, 0) # add NIO on port 0 + nio2 = NIO_Null(frsw.hypervisor) + frsw.add_nio(nio1, 1) # add NIO on port 1 + frsw.map_vc(0, 10, 1, 20) # port 0 DLCI 10 to port 1 DLCI 20 (unidirectional) + frsw.map_vc(1, 20, 0, 10) # port 1 DLCI 20 to port 0 DLCI 10 (unidirectional) + assert frsw.mapping[(0, 10)] == (1, 20) + assert frsw.mapping[(1, 20)] == (0, 10) + frsw.unmap_vc(0, 10, 1, 20) # port 0 DLCI 10 to port 1 DLCI 20 (unidirectional) + frsw.unmap_vc(1, 20, 0, 10) # port 1 DLCI 20 to port 0 DLCI 10 (unidirectional) + frsw.remove_nio(0) + frsw.remove_nio(1) + nio1.delete() + nio2.delete() diff --git a/tests/dynamips/test_hub.py b/tests/dynamips/test_hub.py new file mode 100644 index 00000000..d490cb11 --- /dev/null +++ b/tests/dynamips/test_hub.py @@ -0,0 +1,25 @@ +from gns3server.modules.dynamips import Hub +from gns3server.modules.dynamips import NIO_Null +import pytest + + +@pytest.fixture +def hub(request, hypervisor): + + hub = Hub(hypervisor, "hub") + request.addfinalizer(hub.delete) + return hub + + +def test_hub_exists(hub): + + assert hub.list() + + +def test_add_remove_nio(hub): + + nio = NIO_Null(hub.hypervisor) + hub.add_nio(nio, 0) # add NIO to port 0 + assert hub.mapping[0] == nio + hub.remove_nio(0) # remove NIO from port 0 + nio.delete() diff --git a/tests/dynamips/test_hypervisor.py b/tests/dynamips/test_hypervisor.py new file mode 100644 index 00000000..d89da84e --- /dev/null +++ b/tests/dynamips/test_hypervisor.py @@ -0,0 +1,44 @@ +from gns3server.modules.dynamips import Hypervisor +import time +import os + + +def test_is_started(hypervisor): + + assert hypervisor.is_running() + + +def test_port(hypervisor): + + assert hypervisor.port == 9000 + + +def test_host(hypervisor): + + assert hypervisor.host == "127.0.0.1" + + +def test_workingdir(hypervisor): + + assert hypervisor.workingdir == "/tmp" + + +def test_path(hypervisor): + + cwd = os.path.dirname(os.path.abspath(__file__)) + dynamips_path = os.path.join(cwd, "dynamips.stable") + assert hypervisor.path == dynamips_path + + +def test_stdout(): + + # try to launch Dynamips on the same port + # this will fail so that we can read its stdout/stderr + cwd = os.path.dirname(os.path.abspath(__file__)) + dynamips_path = os.path.join(cwd, "dynamips.stable") + hypervisor = Hypervisor(dynamips_path, "/tmp", "172.0.0.1", 7200) + hypervisor.start() + # give some time for Dynamips to start + time.sleep(0.01) + output = hypervisor.read_stdout() + assert output diff --git a/tests/dynamips/test_hypervisor_manager.py b/tests/dynamips/test_hypervisor_manager.py new file mode 100644 index 00000000..658cb9b9 --- /dev/null +++ b/tests/dynamips/test_hypervisor_manager.py @@ -0,0 +1,51 @@ +from gns3server.modules.dynamips import Router +from gns3server.modules.dynamips import HypervisorManager +import pytest +import os + + +@pytest.fixture(scope="module") +def hypervisor_manager(request): + + cwd = os.path.dirname(os.path.abspath(__file__)) + dynamips_path = os.path.join(cwd, "dynamips.stable") + print("\nStarting Dynamips Hypervisor: {}".format(dynamips_path)) + manager = HypervisorManager(dynamips_path, "/tmp", "127.0.0.1", 9000) + #manager.start_new_hypervisor() + + def stop(): + print("\nStopping Dynamips Hypervisor") + manager.stop_all_hypervisors() + + request.addfinalizer(stop) + return manager + + +def test_allocate_hypervisor_for_router(hypervisor_manager): + + # default of 1GB of RAM per hypervisor instance + assert hypervisor_manager.memory_usage_limit_per_hypervisor == 1024 + hypervisor = hypervisor_manager.allocate_hypervisor_for_router("c3725.image", 512) + assert hypervisor.is_running() + hypervisor = hypervisor_manager.allocate_hypervisor_for_router("c3725.image", 256) + assert hypervisor.memory_load == 768 + hypervisor = hypervisor_manager.allocate_hypervisor_for_router("c3725.image", 512) + assert hypervisor.memory_load == 512 + assert len(hypervisor_manager.hypervisors) == 2 + + +def test_unallocate_hypervisor_for_router(hypervisor_manager): + + assert len(hypervisor_manager.hypervisors) == 2 + hypervisor = hypervisor_manager.hypervisors[0] + assert hypervisor.memory_load == 768 + router = Router(hypervisor, "router", "c3725") # default is 128MB of RAM + hypervisor_manager.unallocate_hypervisor_for_router(router) + assert hypervisor.memory_load == 640 + hypervisor.decrease_memory_load(512) # forces memory load down to 128 + assert hypervisor.memory_load == 128 + router.delete() + hypervisor_manager.unallocate_hypervisor_for_router(router) + # router is deleted and memory load to 0 now, one hypervisor must + # have been shutdown + assert len(hypervisor_manager.hypervisors) == 1 diff --git a/tests/dynamips/test_nios.py b/tests/dynamips/test_nios.py new file mode 100644 index 00000000..0538c298 --- /dev/null +++ b/tests/dynamips/test_nios.py @@ -0,0 +1,139 @@ +from gns3server.modules.dynamips import NIO_UDP +from gns3server.modules.dynamips import NIO_UDP_auto +from gns3server.modules.dynamips import NIO_FIFO +from gns3server.modules.dynamips import NIO_Mcast +from gns3server.modules.dynamips import NIO_Null +from gns3server.modules.dynamips import DynamipsError +import pytest + +# TODO: test UNIX, TAP, VDE, generic Ethernet and Linux Ethernet NIOs + + +def test_nio_udp(hypervisor): + + nio1 = NIO_UDP(hypervisor, 10001, "127.0.0.1", 10002) + assert nio1.lport == 10001 + nio2 = NIO_UDP(hypervisor, 10002, "127.0.0.1", 10001) + assert nio2.lport == 10002 + nio1.delete() + nio2.delete() + + +def test_nio_udp_auto(hypervisor): + + nio1 = NIO_UDP_auto(hypervisor, "127.0.0.1", 10001, 10010) + assert nio1.lport == 10001 + nio2 = NIO_UDP_auto(hypervisor, "127.0.0.1", 10001, 10010) + assert nio2.lport == 10002 + nio1.connect("127.0.0.1", nio2.lport) + nio2.connect("127.0.0.1", nio1.lport) + nio1.delete() + nio2.delete() + + +def test_nio_fifo(hypervisor): + + nio1 = NIO_FIFO(hypervisor) + nio2 = NIO_FIFO(hypervisor) + nio1.crossconnect(nio2) + assert nio1.list() + nio1.delete() + nio2.delete() + + +def test_nio_mcast(hypervisor): + + nio1 = NIO_Mcast(hypervisor, "232.0.0.1", 10001) + assert nio1.group == "232.0.0.1" + assert nio1.port == 10001 + nio1.ttl = 254 + assert nio1.ttl == 254 + nio2 = NIO_UDP(hypervisor, 10002, "232.0.0.1", 10001) + nio1.delete() + nio2.delete() + + +def test_nio_null(hypervisor): + + nio = NIO_Null(hypervisor) + assert nio.list() + nio.delete() + + +def test_rename_nio(hypervisor): + + nio = NIO_Null(hypervisor) + assert nio.name.startswith("nio_null") + nio.rename("test") + assert nio.name == "test" + nio.delete() + + +def test_debug_nio(hypervisor): + + nio = NIO_Null(hypervisor) + nio.debug(1) + nio.debug(0) + nio.delete() + + +def test_bind_unbind_filter(hypervisor): + + nio = NIO_Null(hypervisor) + nio.bind_filter("both", "freq_drop") + assert nio.input_filter == ("freq_drop", None) + assert nio.output_filter == ("freq_drop", None) + nio.unbind_filter("both") + nio.bind_filter("in", "capture") + assert nio.input_filter == ("capture", None) + nio.unbind_filter("in") + nio.delete() + + +def test_bind_unknown_filter(hypervisor): + + nio = NIO_Null(hypervisor) + with pytest.raises(DynamipsError): + nio.bind_filter("both", "my_filter") + nio.delete() + + +def test_unbind_with_no_filter_applied(hypervisor): + + nio = NIO_Null(hypervisor) + with pytest.raises(DynamipsError): + nio.unbind_filter("out") + nio.delete() + + +def test_setup_filter(hypervisor): + + nio = NIO_Null(hypervisor) + nio.bind_filter("in", "freq_drop") + nio.setup_filter("in", "5") # drop every 5th packet + assert nio.input_filter == ("freq_drop", "5") + nio.unbind_filter("in") + nio.delete() + + +def test_get_stats(hypervisor): + + nio = NIO_Null(hypervisor) + assert nio.get_stats() == "0 0 0 0" # nothing has been transmitted or received + nio.delete() + + +def test_reset_stats(hypervisor): + + nio = NIO_Null(hypervisor) + nio.reset_stats() + nio.delete() + + +def test_set_bandwidth(hypervisor): + + nio = NIO_Null(hypervisor) + assert nio.bandwidth == None # no constraint by default + nio.set_bandwidth(1000) # bandwidth = 1000 Kb/s + assert nio.bandwidth == 1000 + nio.delete() diff --git a/tests/dynamips/test_router.py b/tests/dynamips/test_router.py new file mode 100644 index 00000000..7540caac --- /dev/null +++ b/tests/dynamips/test_router.py @@ -0,0 +1,229 @@ +from gns3server.modules.dynamips import Router +from gns3server.modules.dynamips import DynamipsError +import pytest +import tempfile +import base64 + + +@pytest.fixture +def router(request, hypervisor): + + router = Router(hypervisor, "router", "c3725") + request.addfinalizer(router.delete) + return router + + +def test_hypervisor_is_started(hypervisor): + + assert hypervisor.is_running() + + +def test_create_and_delete_router(hypervisor): + + router = Router(hypervisor, "test my router") + assert router.id >= 0 + assert router.name == "test my router" + assert router.platform == "c7200" # default platform + assert not router.is_running() + router.delete() + with pytest.raises(DynamipsError): + router.get_status() + + +# def test_rename_router(router): +# +# assert router.name == "router" +# router.rename("my_router") +# assert router.name == "my_router" +# router.rename("router") +# assert router.name == "router" + #router.delete() # FIXME: fails with current Dynamips version + + +def test_image(router): + + # let's pretend this file is an IOS image + with tempfile.NamedTemporaryFile() as ios_image: + router.image = ios_image.name + assert router.image == ios_image.name + + +def test_set_config(router): + + with tempfile.NamedTemporaryFile() as startup_config: + startup_config.write(b"hostname test_config\n") + router.set_config(startup_config.name) + + +def test_push_config(router): + + startup_config = base64.b64encode(b"hostname test_config\n").decode("utf-8") + private_config = base64.b64encode(b"private config\n").decode("utf-8") + router.push_config(startup_config, private_config) + router_startup_config, router_private_config = router.extract_config() + assert startup_config == router_startup_config + assert private_config == router_private_config + + +def test_status(router, image): + # don't test if we have no IOS image + if not image: + return + + assert router.get_status() == "inactive" + router.ram = 256 + router.image = image + router.start() + assert router.is_running() + router.suspend() + assert router.get_status() == "suspended" + router.resume() + assert router.is_running() + router.stop() + assert router.get_status() == "inactive" + + +def test_ram(router): + + assert router.ram == 128 # default ram + router.ram = 256 + assert router.ram == 256 + + +def test_nvram(router): + + assert router.nvram == 128 # default nvram + router.nvram = 256 + assert router.nvram == 256 + + +def test_mmap(router): + + assert router.mmap == True # default value + router.mmap = False + assert router.mmap == False + + +def test_sparsemem(router): + + assert router.sparsemem == True # default value + router.sparsemem = False + assert router.sparsemem == False + + +def test_clock_divisor(router): + + assert router.clock_divisor == 8 # default value + router.clock_divisor = 4 + assert router.clock_divisor == 4 + + +def test_idlepc(router): + + assert router.idlepc == "" # no default value + router.idlepc = "0x60c086a8" + assert router.idlepc == "0x60c086a8" + + +def test_idlemax(router): + + assert router.idlemax == 1500 # default value + router.idlemax = 500 + assert router.idlemax == 500 + + +def test_idlesleep(router): + + assert router.idlesleep == 30 # default value + router.idlesleep = 15 + assert router.idlesleep == 15 + + +def test_exec_area(router): + + assert router.exec_area == None # default value + router.exec_area = 64 + assert router.exec_area == 64 + + +def test_disk0(router): + + assert router.disk0 == 0 # default value + router.disk0 = 16 + assert router.disk0 == 16 + + +def test_disk1(router): + + assert router.disk1 == 0 # default value + router.disk1 = 16 + assert router.disk1 == 16 + + +def test_confreg(router): + + assert router.confreg == "0x2102" # default value + router.confreg = "0x2142" + assert router.confreg == "0x2142" + + +def test_console(router): + + assert router.console == router.hypervisor.baseconsole + router.id + new_console_port = router.console + 100 + router.console = new_console_port + assert router.console == new_console_port + + +def test_aux(router): + + assert router.aux == router.hypervisor.baseaux + router.id + new_aux_port = router.aux + 100 + router.aux = new_aux_port + assert router.aux == new_aux_port + + +def test_cpu_info(router): + + router.get_cpu_info() # nothing is returned by the hypervisor, cannot test? + + +def test_cpu_usage(router): + + usage = router.get_cpu_usage() + assert usage == 0 # router isn't running, so usage must be 0 + + +def test_get_slot_bindings(router): + + assert router.get_slot_bindings()[0] == "0/0: GT96100-FE" + + +def test_get_slot_nio_bindings(router): + + router.get_slot_nio_bindings(slot_id=0) + + +def test_mac_addr(router): + + assert router.mac_addr == None # default value + router.mac_addr = "aa:aa:aa:aa:aa:aa" + assert router.mac_addr == "aa:aa:aa:aa:aa:aa" + + +def test_bogus_mac_addr(router): + + with pytest.raises(DynamipsError): + router.mac_addr = "zz:zz:zz:zz:zz:zz" + + +def test_system_id(router): + + assert router.system_id == None # default value + router.system_id = "FTX0945W0MO" + assert router.system_id == "FTX0945W0MO" + + +def test_get_hardware_info(router): + + router.get_hardware_info() diff --git a/tests/dynamips/test_vmhandler.py b/tests/dynamips/test_vmhandler.py new file mode 100644 index 00000000..cdc4998c --- /dev/null +++ b/tests/dynamips/test_vmhandler.py @@ -0,0 +1,65 @@ +from tornado.testing import AsyncHTTPTestCase +#from gns3server.plugins.dynamips import Dynamips +#from gns3server._compat import urlencode +from functools import partial +import tornado.web +import json +import tempfile + + +# class TestVMHandler(AsyncHTTPTestCase): +# +# def setUp(self): +# +# AsyncHTTPTestCase.setUp(self) +# self.post_request = partial(self.http_client.fetch, +# self.get_url("/api/vms/dynamips"), +# self.stop, +# method="POST") +# +# def get_app(self): +# return tornado.web.Application(Dynamips().handlers()) +# +# def test_endpoint(self): +# self.http_client.fetch(self.get_url("/api/vms/dynamips"), self.stop) +# response = self.wait() +# assert response.code == 200 +# +# def test_upload(self): +# +# try: +# from poster.encode import multipart_encode +# except ImportError: +# # poster isn't available for Python 3, let's just ignore the test +# return +# +# file_to_upload = tempfile.NamedTemporaryFile() +# data, headers = multipart_encode({"file1": file_to_upload}) +# body = "" +# for d in data: +# body += d +# +# response = self.fetch('/api/vms/dynamips/storage/upload', +# headers=headers, +# body=body, +# method='POST') +# +# assert response.code == 200 +# +# def get_new_ioloop(self): +# return tornado.ioloop.IOLoop.instance() +# +# def test_create_vm(self): +# +# post_data = {"name": "R1", +# "platform": "c3725", +# "console": 2000, +# "aux": 3000, +# "image": "c3725.bin", +# "ram": 128} +# +# self.post_request(body=json.dumps(post_data)) +# response = self.wait() +# assert(response.headers['Content-Type'].startswith('application/json')) +# expected = {"success": True} +# assert response.body.decode("utf-8") == json.dumps(expected)