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 ATM switch module ( " atmsw " ) .
http : / / github . com / GNS3 / dynamips / blob / master / README . hypervisor #L593
"""
import asyncio
2015-02-15 21:18:12 +02:00
import re
2015-02-14 05:01:18 +02:00
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 ATMSwitch ( Device ) :
2015-02-23 18:21:39 +02:00
2015-02-14 05:01:18 +02:00
"""
Dynamips ATM 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 ' atmsw create " { self . _name } " ' )
log . info ( f ' ATM 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 ATM switch .
: param new_name : New name for this switch
"""
2021-04-13 12:07:58 +03:00
await self . _hypervisor . send ( f ' atmsw rename " { self . _name } " " { new_name } " ' )
2021-04-13 12:16:50 +03:00
log . info (
' ATM 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 ATM 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 ' atmsw delete " { self . _name } " ' )
log . info ( f ' ATM 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 ATM switch { self . _name } " )
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
return True
2018-10-15 13:05:49 +03:00
async def delete ( self ) :
2016-12-12 20:17:06 +02:00
"""
Deletes this ATM switch .
"""
2018-10-15 13:05:49 +03:00
await self . close ( )
2015-02-14 05:01:18 +02:00
def has_port ( self , port ) :
"""
Checks if a port exists on this ATM switch .
: returns : boolean
"""
if port in self . _nios :
return True
return False
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 ATM 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 (
' ATM switch " {name} " [id= {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 ATM switch .
: param port_number : allocated port number
"""
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 08:09:26 +03:00
if len ( source ) == 3 and len ( destination ) == 3 :
2015-04-13 03:09:53 +03:00
# remove the virtual channels mapped with this port/nio
2017-05-12 08:09:26 +03:00
source_port , source_vpi , source_vci = source
destination_port , destination_vpi , destination_vci = destination
2015-04-13 03:09:53 +03:00
if port_number == source_port :
2021-04-13 12:16:50 +03:00
log . info (
' ATM switch " {name} " [ {id} ]: unmapping VCC between port {source_port} VPI {source_vpi} VCI {source_vci} and port {destination_port} VPI {destination_vpi} VCI {destination_vci} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_vpi = source_vpi ,
source_vci = source_vci ,
destination_port = destination_port ,
destination_vpi = destination_vpi ,
destination_vci = destination_vci ,
)
)
await self . unmap_pvc (
source_port , source_vpi , source_vci , destination_port , destination_vpi , destination_vci
)
await self . unmap_pvc (
destination_port , destination_vpi , destination_vci , source_port , source_vpi , source_vci
)
2015-04-13 03:09:53 +03:00
else :
# remove the virtual paths mapped with this port/nio
2017-05-12 08:09:26 +03:00
source_port , source_vpi = source
destination_port , destination_vpi = destination
2015-04-13 03:09:53 +03:00
if port_number == source_port :
2021-04-13 12:16:50 +03:00
log . info (
' ATM switch " {name} " [ {id} ]: unmapping VPC between port {source_port} VPI {source_vpi} and port {destination_port} VPI {destination_vpi} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_vpi = source_vpi ,
destination_port = destination_port ,
destination_vpi = destination_vpi ,
)
)
2018-10-15 13:05:49 +03:00
await self . unmap_vp ( source_port , source_vpi , destination_port , destination_vpi )
await self . unmap_vp ( destination_port , destination_vpi , source_port , source_vpi )
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 )
2021-04-13 12:16:50 +03:00
log . info (
' ATM 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 )
"""
pvc_entry = re . compile ( r """ ^([0-9]*):([0-9]*):([0-9]*)$ """ )
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 ATM mappings " )
2015-02-15 21:18:12 +02:00
match_source_pvc = pvc_entry . search ( source )
match_destination_pvc = pvc_entry . search ( destination )
if match_source_pvc and match_destination_pvc :
# add the virtual channels
source_port , source_vpi , source_vci = map ( int , match_source_pvc . group ( 1 , 2 , 3 ) )
destination_port , destination_vpi , destination_vci = map ( int , match_destination_pvc . group ( 1 , 2 , 3 ) )
if self . has_port ( destination_port ) :
2021-04-13 12:16:50 +03:00
if ( source_port , source_vpi , source_vci ) not in self . _active_mappings and (
destination_port ,
destination_vpi ,
destination_vci ,
) not in self . _active_mappings :
log . info (
' ATM switch " {name} " [ {id} ]: mapping VCC between port {source_port} VPI {source_vpi} VCI {source_vci} and port {destination_port} VPI {destination_vpi} VCI {destination_vci} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_vpi = source_vpi ,
source_vci = source_vci ,
destination_port = destination_port ,
destination_vpi = destination_vpi ,
destination_vci = destination_vci ,
)
)
await self . map_pvc (
source_port , source_vpi , source_vci , destination_port , destination_vpi , destination_vci
)
await self . map_pvc (
destination_port , destination_vpi , destination_vci , source_port , source_vpi , source_vci
)
2015-02-15 21:18:12 +02:00
else :
# add the virtual paths
2021-04-13 12:16:50 +03:00
source_port , source_vpi = map ( int , source . split ( " : " ) )
destination_port , destination_vpi = 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_vpi ) not in self . _active_mappings and (
destination_port ,
destination_vpi ,
) not in self . _active_mappings :
log . info (
' ATM switch " {name} " [ {id} ]: mapping VPC between port {source_port} VPI {source_vpi} and port {destination_port} VPI {destination_vpi} ' . format (
name = self . _name ,
id = self . _id ,
source_port = source_port ,
source_vpi = source_vpi ,
destination_port = destination_port ,
destination_vpi = destination_vpi ,
)
)
2018-10-15 13:05:49 +03:00
await self . map_vp ( source_port , source_vpi , destination_port , destination_vpi )
await self . map_vp ( destination_port , destination_vpi , source_port , source_vpi )
2015-02-15 21:18:12 +02:00
2018-10-15 13:05:49 +03:00
async def map_vp ( self , port1 , vpi1 , port2 , vpi2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2017-05-12 13:00:58 +03:00
return
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2017-05-12 13:00:58 +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 (
' 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
)
)
2015-02-14 05:01:18 +02:00
2021-04-13 12:16:50 +03:00
log . info (
' ATM switch " {name} " [ {id} ]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} created ' . format (
name = self . _name , id = self . _id , port1 = port1 , vpi1 = vpi1 , port2 = port2 , vpi2 = vpi2
)
)
2015-02-14 05:01:18 +02:00
2017-05-12 08:09:26 +03:00
self . _active_mappings [ ( port1 , vpi1 ) ] = ( port2 , vpi2 )
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def unmap_vp ( self , port1 , vpi1 , port2 , vpi2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2017-05-12 13:00:58 +03:00
return
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2017-05-12 13:00:58 +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 (
' 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
)
)
2015-02-14 05:01:18 +02:00
2021-04-13 12:16:50 +03:00
log . info (
' ATM switch " {name} " [ {id} ]: VPC from port {port1} VPI {vpi1} to port {port2} VPI {vpi2} deleted ' . format (
name = self . _name , id = self . _id , port1 = port1 , vpi1 = vpi1 , port2 = port2 , vpi2 = vpi2
)
)
2015-02-14 05:01:18 +02:00
2017-05-12 08:09:26 +03:00
del self . _active_mappings [ ( port1 , vpi1 ) ]
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def map_pvc ( self , port1 , vpi1 , vci1 , port2 , vpi2 , vci2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2017-05-12 13:00:58 +03:00
return
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2017-05-12 13:00:58 +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 (
' atmsw create_vcc " {name} " {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci} ' . format (
name = self . _name ,
input_nio = nio1 ,
input_vpi = vpi1 ,
input_vci = vci1 ,
output_nio = nio2 ,
output_vpi = vpi2 ,
output_vci = vci2 ,
)
)
log . info (
' ATM switch " {name} " [ {id} ]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} created ' . format (
name = self . _name , id = self . _id , port1 = port1 , vpi1 = vpi1 , vci1 = vci1 , port2 = port2 , vpi2 = vpi2 , vci2 = vci2
)
)
2015-02-14 05:01:18 +02:00
2017-05-12 08:09:26 +03:00
self . _active_mappings [ ( port1 , vpi1 , vci1 ) ] = ( port2 , vpi2 , vci2 )
2015-02-14 05:01:18 +02:00
2018-10-15 13:05:49 +03:00
async def unmap_pvc ( self , port1 , vpi1 , vci1 , port2 , vpi2 , vci2 ) :
2015-02-14 05:01:18 +02:00
"""
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 :
2017-05-12 13:00:58 +03:00
return
2015-02-14 05:01:18 +02:00
if port2 not in self . _nios :
2017-05-12 13:00:58 +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 (
' atmsw delete_vcc " {name} " {input_nio} {input_vpi} {input_vci} {output_nio} {output_vpi} {output_vci} ' . format (
name = self . _name ,
input_nio = nio1 ,
input_vpi = vpi1 ,
input_vci = vci1 ,
output_nio = nio2 ,
output_vpi = vpi2 ,
output_vci = vci2 ,
)
)
log . info (
' ATM switch " {name} " [ {id} ]: VCC from port {port1} VPI {vpi1} VCI {vci1} to port {port2} VPI {vpi2} VCI {vci2} deleted ' . format (
name = self . _name , id = self . _id , port1 = port1 , vpi1 = vpi1 , vci1 = vci1 , port2 = port2 , vpi2 = vpi2 , vci2 = vci2
)
)
2017-05-12 08:09:26 +03:00
del self . _active_mappings [ ( port1 , vpi1 , vci1 ) ]
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_ATM_RFC1483 " ) :
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_ATM_RFC1483
"""
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 (
' ATM 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 (
' ATM switch " {name} " [ {id} ]: stopping packet capture on port {port} ' . format (
name = self . _name , id = self . _id , port = port_number
)
)