2015-02-14 05:01:18 +02:00
#
# Copyright (C) 2013 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Interface for Dynamips virtual Frame - Relay switch module .
http : / / github . com / GNS3 / dynamips / blob / master / README . hypervisor #L642
"""
import asyncio
from . device import Device
2015-02-24 04:00:34 +02:00
from . . nios . nio_udp import NIOUDP
2015-02-14 05:01:18 +02:00
from . . dynamips_error import DynamipsError
import logging
2021-04-13 12:16:50 +03:00
2015-02-14 05:01:18 +02:00
log = logging . getLogger ( __name__ )
class FrameRelaySwitch ( Device ) :
2015-02-23 18:21:39 +02:00
2015-02-14 05:01:18 +02:00
"""
Dynamips Frame Relay switch .
: param name : name for this switch
2016-05-21 02:59:59 +03:00
: param node_id : Node instance identifier
2015-02-14 05:01:18 +02:00
: param project : Project instance
: param manager : Parent VM Manager
: param hypervisor : Dynamips hypervisor instance
"""
2016-05-21 02:59:59 +03:00
def __init__ ( self , name , node_id , project , manager , mappings = None , hypervisor = None ) :
2015-02-14 05:01:18 +02:00
2016-05-21 02:59:59 +03:00
super ( ) . __init__ ( name , node_id , project , manager , hypervisor )
2015-02-14 05:01:18 +02:00
self . _nios = { }
2016-05-21 02:59:59 +03:00
self . _active_mappings = { }
2015-02-15 21:18:12 +02:00
self . _mappings = { }
2016-05-21 02:59:59 +03:00
if mappings :
self . _mappings = mappings
2015-02-15 21:18:12 +02:00
2021-04-17 17:04:28 +03:00
def asdict ( self ) :
2015-02-15 21:18:12 +02:00
2015-09-08 11:00:39 +03:00
mappings = { }
for source , destination in self . _mappings . items ( ) :
2016-05-21 02:59:59 +03:00
mappings [ source ] = destination
2015-09-08 11:00:39 +03:00
2021-04-13 12:16:50 +03:00
return {
" name " : self . name ,
" usage " : self . usage ,
" node_id " : self . id ,
" project_id " : self . project . id ,
" mappings " : mappings ,
" status " : " started " ,
}
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def create ( self ) :
2015-02-14 05:01:18 +02:00
if self . _hypervisor is None :
2015-02-16 07:13:24 +02:00
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
2018-10-15 13:05:49 +03:00
self . _hypervisor = await self . manager . start_new_hypervisor ( working_dir = module_workdir )
2015-02-14 05:01:18 +02:00
2021-04-13 12:07:58 +03:00
await self . _hypervisor . send ( f ' frsw create " { self . _name } " ' )
log . info ( f ' Frame Relay switch " { self . _name } " [ { self . _id } ] has been created ' )
2015-02-14 05:01:18 +02:00
self . _hypervisor . devices . append ( self )
2018-10-15 13:05:49 +03:00
async def set_name ( self , new_name ) :
2015-02-14 05:01:18 +02:00
"""
Renames this Frame Relay switch .
: param new_name : New name for this switch
"""
2021-04-13 12:07:58 +03:00
await self . _hypervisor . send ( f ' frsw rename " { self . _name } " " { new_name } " ' )
2021-04-13 12:16:50 +03:00
log . info (
' Frame Relay switch " {name} " [ {id} ]: renamed to " {new_name} " ' . format (
name = self . _name , id = self . _id , new_name = new_name
)
)
2015-02-14 05:01:18 +02:00
self . _name = new_name
@property
def nios ( self ) :
"""
Returns all the NIOs member of this Frame Relay switch .
: returns : nio list
"""
return self . _nios
@property
2015-02-15 21:18:12 +02:00
def mappings ( self ) :
2015-02-14 05:01:18 +02:00
"""
2015-02-15 21:18:12 +02:00
Returns port mappings
2015-02-14 05:01:18 +02:00
2015-02-15 21:18:12 +02:00
: returns : mappings list
2015-02-14 05:01:18 +02:00
"""
2015-02-15 21:18:12 +02:00
return self . _mappings
2015-02-14 05:01:18 +02:00
2016-05-21 02:59:59 +03:00
@mappings.setter
def mappings ( self , mappings ) :
"""
Sets port mappings
: param mappings : mappings list
"""
self . _mappings = mappings
2018-10-15 13:05:49 +03:00
async def close ( self ) :
2015-02-24 04:00:34 +02:00
for nio in self . _nios . values ( ) :
2017-07-10 21:38:28 +03:00
if nio :
2018-10-15 13:05:49 +03:00
await nio . close ( )
2015-02-24 04:00:34 +02:00
2016-12-12 20:17:06 +02:00
if self . _hypervisor :
try :
2021-04-13 12:07:58 +03:00
await self . _hypervisor . send ( f ' frsw delete " { self . _name } " ' )
log . info ( f ' Frame Relay switch " { self . _name } " [ { self . _id } ] has been deleted ' )
2016-12-12 20:17:06 +02:00
except DynamipsError :
2021-04-13 12:07:58 +03:00
log . debug ( f " Could not properly delete Frame relay switch { self . _name } " )
2016-12-12 20:17:06 +02:00
2015-02-25 20:52:52 +02:00
if self . _hypervisor and self in self . _hypervisor . devices :
self . _hypervisor . devices . remove ( self )
2015-02-15 21:18:12 +02:00
if self . _hypervisor and not self . _hypervisor . devices :
2018-10-15 13:05:49 +03:00
await self . hypervisor . stop ( )
2016-12-12 20:17:06 +02:00
self . _hypervisor = None
2018-10-15 13:05:49 +03:00
async def delete ( self ) :
2016-12-12 20:17:06 +02:00
"""
Deletes this Frame Relay switch .
"""
2018-10-15 13:05:49 +03:00
await self . close ( )
2016-12-12 20:17:06 +02:00
return True
2015-02-14 05:01:18 +02:00
def has_port ( self , port ) :
"""
Checks if a port exists on this Frame Relay switch .
: returns : boolean
"""
if port in self . _nios :
return True
return False
2018-10-15 13:05:49 +03:00
async def add_nio ( self , nio , port_number ) :
2015-02-14 05:01:18 +02:00
"""
Adds a NIO as new port on Frame Relay switch .
: param nio : NIO instance to add
: param port_number : port to allocate for the NIO
"""
if port_number in self . _nios :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port_number } isn ' t free " )
2015-02-14 05:01:18 +02:00
2021-04-13 12:16:50 +03:00
log . info (
' Frame Relay switch " {name} " [ {id} ]: NIO {nio} bound to port {port} ' . format (
name = self . _name , id = self . _id , nio = nio , port = port_number
)
)
2015-02-14 05:01:18 +02:00
self . _nios [ port_number ] = nio
2018-10-15 13:05:49 +03:00
await self . set_mappings ( self . _mappings )
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def remove_nio ( self , port_number ) :
2015-02-14 05:01:18 +02:00
"""
Removes the specified NIO as member of this Frame Relay switch .
: param port_number : allocated port number
: returns : the NIO that was bound to the allocated port
"""
if port_number not in self . _nios :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port_number } is not allocated " )
2015-02-14 05:01:18 +02:00
2019-04-01 15:47:31 +03:00
await self . stop_capture ( port_number )
2015-04-13 03:09:53 +03:00
# remove VCs mapped with the port
2016-05-21 02:59:59 +03:00
for source , destination in self . _active_mappings . copy ( ) . items ( ) :
2017-05-12 07:39:01 +03:00
source_port , source_dlci = source
destination_port , destination_dlci = destination
2015-04-13 03:09:53 +03:00
if port_number == source_port :
2021-04-13 12:16:50 +03:00
log . info (
' Frame Relay switch " {name} " [ {id} ]: unmapping VC between port {source_port} DLCI {source_dlci} and port {destination_port} DLCI {destination_dlci} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_dlci = source_dlci ,
destination_port = destination_port ,
destination_dlci = destination_dlci ,
)
)
2018-10-15 13:05:49 +03:00
await self . unmap_vc ( source_port , source_dlci , destination_port , destination_dlci )
await self . unmap_vc ( destination_port , destination_dlci , source_port , source_dlci )
2015-04-13 03:09:53 +03:00
2015-02-14 05:01:18 +02:00
nio = self . _nios [ port_number ]
2015-02-24 04:00:34 +02:00
if isinstance ( nio , NIOUDP ) :
2015-03-22 01:19:12 +02:00
self . manager . port_manager . release_udp_port ( nio . lport , self . _project )
2015-02-24 04:00:34 +02:00
2021-04-13 12:16:50 +03:00
log . info (
' Frame Relay switch " {name} " [ {id} ]: NIO {nio} removed from port {port} ' . format (
name = self . _name , id = self . _id , nio = nio , port = port_number
)
)
2015-02-14 05:01:18 +02:00
del self . _nios [ port_number ]
return nio
2018-10-27 10:47:17 +03:00
def get_nio ( self , port_number ) :
"""
Gets a port NIO binding .
: param port_number : port number
: returns : NIO instance
"""
if port_number not in self . _nios :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port_number } is not allocated " )
2018-10-27 10:47:17 +03:00
nio = self . _nios [ port_number ]
if not nio :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port_number } is not connected " )
2018-10-27 10:47:17 +03:00
return nio
2018-10-15 13:05:49 +03:00
async def set_mappings ( self , mappings ) :
2015-02-15 21:18:12 +02:00
"""
Applies VC mappings
: param mappings : mappings ( dict )
"""
for source , destination in mappings . items ( ) :
2015-09-08 11:00:39 +03:00
if not isinstance ( source , str ) or not isinstance ( destination , str ) :
raise DynamipsError ( " Invalid Frame-Relay mappings " )
2021-04-13 12:16:50 +03:00
source_port , source_dlci = map ( int , source . split ( " : " ) )
destination_port , destination_dlci = map ( int , destination . split ( " : " ) )
2015-02-15 21:18:12 +02:00
if self . has_port ( destination_port ) :
2021-04-13 12:16:50 +03:00
if ( source_port , source_dlci ) not in self . _active_mappings and (
destination_port ,
destination_dlci ,
) not in self . _active_mappings :
log . info (
' Frame Relay switch " {name} " [ {id} ]: mapping VC between port {source_port} DLCI {source_dlci} and port {destination_port} DLCI {destination_dlci} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_dlci = source_dlci ,
destination_port = destination_port ,
destination_dlci = destination_dlci ,
)
)
2017-05-12 07:39:01 +03:00
2018-10-15 13:05:49 +03:00
await self . map_vc ( source_port , source_dlci , destination_port , destination_dlci )
await self . map_vc ( destination_port , destination_dlci , source_port , source_dlci )
2015-02-15 21:18:12 +02:00
2018-10-15 13:05:49 +03:00
async def map_vc ( self , port1 , dlci1 , port2 , dlci2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2017-03-28 12:25:26 +03:00
return
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2017-03-28 12:25:26 +03:00
return
2015-02-14 05:01:18 +02:00
nio1 = self . _nios [ port1 ]
nio2 = self . _nios [ port2 ]
2021-04-13 12:16:50 +03:00
await self . _hypervisor . send (
' frsw create_vc " {name} " {input_nio} {input_dlci} {output_nio} {output_dlci} ' . format (
name = self . _name , input_nio = nio1 , input_dlci = dlci1 , output_nio = nio2 , output_dlci = dlci2
)
)
2015-02-14 05:01:18 +02:00
2021-04-13 12:16:50 +03:00
log . info (
' Frame Relay switch " {name} " [ {id} ]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} created ' . format (
name = self . _name , id = self . _id , port1 = port1 , dlci1 = dlci1 , port2 = port2 , dlci2 = dlci2
)
)
2015-02-14 05:01:18 +02:00
2017-05-12 07:39:01 +03:00
self . _active_mappings [ ( port1 , dlci1 ) ] = ( port2 , dlci2 )
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def unmap_vc ( self , port1 , dlci1 , port2 , dlci2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port1 } is not allocated " )
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port2 } is not allocated " )
2015-02-14 05:01:18 +02:00
nio1 = self . _nios [ port1 ]
nio2 = self . _nios [ port2 ]
2021-04-13 12:16:50 +03:00
await self . _hypervisor . send (
' frsw delete_vc " {name} " {input_nio} {input_dlci} {output_nio} {output_dlci} ' . format (
name = self . _name , input_nio = nio1 , input_dlci = dlci1 , output_nio = nio2 , output_dlci = dlci2
)
)
log . info (
' Frame Relay switch " {name} " [ {id} ]: VC from port {port1} DLCI {dlci1} to port {port2} DLCI {dlci2} deleted ' . format (
name = self . _name , id = self . _id , port1 = port1 , dlci1 = dlci1 , port2 = port2 , dlci2 = dlci2
)
)
2017-05-12 07:39:01 +03:00
del self . _active_mappings [ ( port1 , dlci1 ) ]
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def start_capture ( self , port_number , output_file , data_link_type = " DLT_FRELAY " ) :
2015-02-14 05:01:18 +02:00
"""
Starts a packet capture .
: param port_number : allocated port number
: param output_file : PCAP destination file for the capture
: param data_link_type : PCAP data link type ( DLT_ * ) , default is DLT_FRELAY
"""
2018-10-27 10:47:17 +03:00
nio = self . get_nio ( port_number )
2015-02-14 05:01:18 +02:00
data_link_type = data_link_type . lower ( )
if data_link_type . startswith ( " dlt_ " ) :
data_link_type = data_link_type [ 4 : ]
if nio . input_filter [ 0 ] is not None and nio . output_filter [ 0 ] is not None :
2021-04-13 12:07:58 +03:00
raise DynamipsError ( f " Port { port_number } has already a filter applied " )
2015-02-14 05:01:18 +02:00
2019-04-01 16:58:18 +03:00
await nio . start_packet_capture ( output_file , data_link_type )
2021-04-13 12:16:50 +03:00
log . info (
' Frame relay switch " {name} " [ {id} ]: starting packet capture on port {port} ' . format (
name = self . _name , id = self . _id , port = port_number
)
)
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def stop_capture ( self , port_number ) :
2015-02-14 05:01:18 +02:00
"""
Stops a packet capture .
: param port_number : allocated port number
"""
2018-10-27 10:47:17 +03:00
nio = self . get_nio ( port_number )
2019-04-01 15:47:31 +03:00
if not nio . capturing :
return
2019-04-01 16:58:18 +03:00
await nio . stop_packet_capture ( )
2021-04-13 12:16:50 +03:00
log . info (
' Frame relay switch " {name} " [ {id} ]: stopping packet capture on port {port} ' . format (
name = self . _name , id = self . _id , port = port_number
)
)