2015-02-11 13:31:21 +00:00
# -*- coding: utf-8 -*-
#
# Copyright (C) 2015 GNS3 Technologies Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
IOU VM management ( creates command line , processes , files etc . ) in
2015-04-08 17:17:34 +00:00
order to run an IOU VM .
2015-02-11 13:31:21 +00:00
"""
import os
2015-03-12 00:59:57 +00:00
import socket
2015-02-11 13:31:21 +00:00
import re
import asyncio
2015-02-16 16:20:07 +00:00
import subprocess
2015-02-11 13:31:21 +00:00
import shutil
2015-02-11 16:11:18 +00:00
import configparser
2015-03-12 00:59:57 +00:00
import struct
import hashlib
2015-02-13 19:57:20 +00:00
import glob
2015-06-06 21:15:03 +00:00
import binascii
2015-12-06 01:24:08 +00:00
import functools
2015-02-11 13:31:21 +00:00
from . iou_error import IOUError
from . . adapters . ethernet_adapter import EthernetAdapter
from . . adapters . serial_adapter import SerialAdapter
2015-02-24 02:00:34 +00:00
from . . nios . nio_udp import NIOUDP
2016-05-11 17:35:36 +00:00
from . . base_node import BaseNode
2015-06-06 21:15:03 +00:00
from . utils . iou_import import nvram_import
2015-10-21 12:28:39 +00:00
from . utils . iou_export import nvram_export
2016-11-06 10:27:49 +00:00
from gns3server . ubridge . ubridge_error import UbridgeError
2016-06-10 16:26:01 +00:00
from gns3server . utils . file_watcher import FileWatcher
2016-11-08 09:21:20 +00:00
from gns3server . utils . asyncio . telnet_server import AsyncioTelnetServer
2018-08-25 07:10:47 +00:00
from gns3server . utils . asyncio import locking
2015-02-16 16:20:07 +00:00
import gns3server . utils . asyncio
2015-06-17 15:11:25 +00:00
import gns3server . utils . images
2015-02-11 13:31:21 +00:00
import logging
2015-03-16 10:52:22 +00:00
import sys
2015-02-11 13:31:21 +00:00
log = logging . getLogger ( __name__ )
2016-05-11 17:35:36 +00:00
class IOUVM ( BaseNode ) :
2015-02-11 13:31:21 +00:00
module_name = ' iou '
"""
2015-04-08 17:17:34 +00:00
IOU VM implementation .
2015-02-11 13:31:21 +00:00
2015-04-08 17:17:34 +00:00
: param name : IOU VM name
2016-05-11 17:35:36 +00:00
: param node_id : Node identifier
2015-02-11 13:31:21 +00:00
: param project : Project instance
2015-04-08 17:17:34 +00:00
: param manager : Manager instance
2015-02-11 13:31:21 +00:00
: param console : TCP console port
2018-03-24 11:11:21 +00:00
: param console_type : console type
2015-02-11 13:31:21 +00:00
"""
2018-03-24 11:11:21 +00:00
def __init__ ( self , name , node_id , project , manager , application_id = None , path = None , console = None , console_type = " telnet " ) :
2015-02-11 13:31:21 +00:00
2018-03-24 11:11:21 +00:00
super ( ) . __init__ ( name , node_id , project , manager , console = console , console_type = console_type )
2015-02-11 13:31:21 +00:00
2020-02-10 07:20:49 +00:00
log . info ( ' IOU " {name} " [ {id} ]: assigned with application ID {application_id} ' . format ( name = self . _name ,
id = self . _id ,
application_id = application_id ) )
2015-02-11 13:31:21 +00:00
self . _iou_process = None
2016-11-08 09:21:20 +00:00
self . _telnet_server = None
2015-02-11 13:31:21 +00:00
self . _iou_stdout_file = " "
self . _started = False
2016-11-06 10:27:49 +00:00
self . _nvram_watcher = None
2018-11-19 08:53:43 +00:00
self . _path = self . manager . get_abs_image_path ( path , project . path )
2018-11-14 08:24:30 +00:00
self . _license_check = True
2015-02-11 13:31:21 +00:00
# IOU settings
2015-02-12 20:02:52 +00:00
self . _ethernet_adapters = [ ]
self . _serial_adapters = [ ]
2015-04-12 21:09:37 +00:00
self . ethernet_adapters = 2 # one adapter = 4 interfaces
self . serial_adapters = 2 # one adapter = 4 interfaces
self . _use_default_iou_values = True # for RAM & NVRAM values
2015-04-15 13:50:34 +00:00
self . _nvram = 128 # Kilobytes
2015-06-06 21:15:03 +00:00
self . _startup_config = " "
self . _private_config = " "
2015-04-15 13:50:34 +00:00
self . _ram = 256 # Megabytes
2018-03-12 06:38:50 +00:00
self . _application_id = application_id
2015-04-12 21:09:37 +00:00
self . _l1_keepalives = False # used to overcome the always-up Ethernet interfaces (not supported by all IOSes).
2015-02-13 21:16:43 +00:00
2016-06-07 13:34:04 +00:00
def _config ( self ) :
return self . _manager . config . get_section_config ( " IOU " )
2016-06-10 16:26:01 +00:00
def _nvram_changed ( self , path ) :
"""
2016-06-11 04:33:07 +00:00
Called when the NVRAM file has changed
2016-06-10 16:26:01 +00:00
"""
2017-01-10 14:50:35 +00:00
log . debug ( " NVRAM changed: {} " . format ( path ) )
2016-06-10 16:26:01 +00:00
self . save_configs ( )
2017-01-10 14:50:35 +00:00
self . updated ( )
2016-06-10 16:26:01 +00:00
2018-10-15 10:05:49 +00:00
async def close ( self ) :
2015-04-08 17:17:34 +00:00
"""
Closes this IOU VM .
"""
2015-02-11 13:31:21 +00:00
2018-10-15 10:05:49 +00:00
if not ( await super ( ) . close ( ) ) :
2016-02-29 09:38:30 +00:00
return False
2015-02-11 13:31:21 +00:00
2015-02-24 02:00:34 +00:00
adapters = self . _ethernet_adapters + self . _serial_adapters
for adapter in adapters :
if adapter is not None :
for nio in adapter . ports . values ( ) :
if nio and isinstance ( nio , NIOUDP ) :
2015-03-21 23:19:12 +00:00
self . manager . port_manager . release_udp_port ( nio . lport , self . _project )
2015-02-24 02:00:34 +00:00
2018-10-15 10:05:49 +00:00
await self . stop ( )
2015-02-24 02:00:34 +00:00
2015-02-11 13:31:21 +00:00
@property
2015-02-12 14:20:47 +00:00
def path ( self ) :
2015-04-08 17:17:34 +00:00
"""
Path of the IOU executable .
: returns : path to the IOU image executable
"""
2015-02-11 13:31:21 +00:00
2015-02-12 14:20:47 +00:00
return self . _path
2015-02-11 13:31:21 +00:00
2015-02-12 14:20:47 +00:00
@path.setter
def path ( self , path ) :
2015-02-11 13:31:21 +00:00
"""
2015-04-08 17:17:34 +00:00
Path of the IOU executable .
2015-02-11 13:31:21 +00:00
2015-04-08 17:17:34 +00:00
: param path : path to the IOU image executable
2015-02-11 13:31:21 +00:00
"""
2018-11-19 08:53:43 +00:00
self . _path = self . manager . get_abs_image_path ( path , self . project . path )
2017-11-16 09:52:19 +00:00
log . info ( ' IOU " {name} " [ {id} ]: IOU image updated to " {path} " ' . format ( name = self . _name , id = self . _id , path = self . _path ) )
2015-04-07 13:25:53 +00:00
2015-02-11 13:31:21 +00:00
@property
def use_default_iou_values ( self ) :
"""
Returns if this device uses the default IOU image values .
2015-04-08 17:17:34 +00:00
2015-02-11 13:31:21 +00:00
: returns : boolean
"""
return self . _use_default_iou_values
@use_default_iou_values.setter
def use_default_iou_values ( self , state ) :
"""
Sets if this device uses the default IOU image values .
2015-04-08 17:17:34 +00:00
2015-02-11 13:31:21 +00:00
: param state : boolean
"""
self . _use_default_iou_values = state
if state :
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: uses the default IOU image values ' . format ( name = self . _name , id = self . _id ) )
2015-02-11 13:31:21 +00:00
else :
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: does not use the default IOU image values ' . format ( name = self . _name , id = self . _id ) )
2015-02-11 13:31:21 +00:00
2018-10-15 10:05:49 +00:00
async def update_default_iou_values ( self ) :
2017-11-16 09:52:19 +00:00
"""
Finds the default RAM and NVRAM values for the IOU image .
"""
try :
2018-10-15 10:05:49 +00:00
output = await gns3server . utils . asyncio . subprocess_check_output ( self . _path , " -h " , cwd = self . working_dir , stderr = True )
2019-01-17 11:01:58 +00:00
match = re . search ( r " -n <n> \ s+Size of nvram in Kb \ (default ([0-9]+)KB \ ) " , output )
2017-11-16 09:52:19 +00:00
if match :
self . nvram = int ( match . group ( 1 ) )
2019-01-17 11:01:58 +00:00
match = re . search ( r " -m <n> \ s+Megabytes of router memory \ (default ([0-9]+)MB \ ) " , output )
2017-11-16 09:52:19 +00:00
if match :
self . ram = int ( match . group ( 1 ) )
except ( ValueError , OSError , subprocess . SubprocessError ) as e :
log . warning ( " could not find default RAM and NVRAM values for {} : {} " . format ( os . path . basename ( self . _path ) , e ) )
2018-10-15 10:05:49 +00:00
async def create ( self ) :
2017-11-16 09:52:19 +00:00
2018-10-15 10:05:49 +00:00
await self . update_default_iou_values ( )
2017-11-16 09:52:19 +00:00
2015-02-11 13:31:21 +00:00
def _check_requirements ( self ) :
"""
2016-11-06 10:27:49 +00:00
Checks the IOU image .
2015-02-11 13:31:21 +00:00
"""
2015-04-08 17:17:34 +00:00
2016-11-02 09:20:43 +00:00
if not self . _path :
raise IOUError ( " IOU image is not configured " )
2015-06-19 14:35:19 +00:00
if not os . path . isfile ( self . _path ) or not os . path . exists ( self . _path ) :
if os . path . islink ( self . _path ) :
raise IOUError ( " IOU image ' {} ' linked to ' {} ' is not accessible " . format ( self . _path , os . path . realpath ( self . _path ) ) )
else :
raise IOUError ( " IOU image ' {} ' is not accessible " . format ( self . _path ) )
try :
with open ( self . _path , " rb " ) as f :
# read the first 7 bytes of the file.
elf_header_start = f . read ( 7 )
except OSError as e :
raise IOUError ( " Cannot read ELF header for IOU image ' {} ' : {} " . format ( self . _path , e ) )
2017-06-23 10:00:33 +00:00
# IOU images must start with the ELF magic number, be 32-bit or 64-bit, little endian
2015-06-19 14:35:19 +00:00
# and have an ELF version of 1 normal IOS image are big endian!
2017-06-23 10:00:33 +00:00
if elf_header_start != b ' \x7f ELF \x01 \x01 \x01 ' and elf_header_start != b ' \x7f ELF \x02 \x01 \x01 ' :
2015-06-19 14:35:19 +00:00
raise IOUError ( " ' {} ' is not a valid IOU image " . format ( self . _path ) )
if not os . access ( self . _path , os . X_OK ) :
raise IOUError ( " IOU image ' {} ' is not executable " . format ( self . _path ) )
2015-02-11 13:31:21 +00:00
def __json__ ( self ) :
2015-03-11 21:04:11 +00:00
iou_vm_info = { " name " : self . name ,
2018-12-30 12:35:24 +00:00
" usage " : self . usage ,
2016-05-11 17:35:36 +00:00
" node_id " : self . id ,
2017-10-02 08:41:57 +00:00
" node_directory " : self . working_path ,
2015-03-11 21:04:11 +00:00
" console " : self . _console ,
2018-03-24 11:11:21 +00:00
" console_type " : self . _console_type ,
2016-05-17 17:51:06 +00:00
" status " : self . status ,
2015-03-11 21:04:11 +00:00
" project_id " : self . project . id ,
" path " : self . path ,
2015-06-17 15:11:25 +00:00
" md5sum " : gns3server . utils . images . md5sum ( self . path ) ,
2015-03-11 21:04:11 +00:00
" ethernet_adapters " : len ( self . _ethernet_adapters ) ,
" serial_adapters " : len ( self . _serial_adapters ) ,
" ram " : self . _ram ,
" nvram " : self . _nvram ,
" l1_keepalives " : self . _l1_keepalives ,
2016-02-02 17:25:17 +00:00
" use_default_iou_values " : self . _use_default_iou_values ,
2017-06-27 09:11:07 +00:00
" command_line " : self . command_line ,
" application_id " : self . application_id }
2015-03-11 21:04:11 +00:00
2018-11-19 08:53:43 +00:00
iou_vm_info [ " path " ] = self . manager . get_relative_image_path ( self . path , self . project . path )
2015-03-11 21:04:11 +00:00
return iou_vm_info
2015-02-11 13:31:21 +00:00
2015-02-17 15:40:45 +00:00
@property
def iourc_path ( self ) :
"""
2015-04-08 17:17:34 +00:00
Returns the IOURC file path .
2015-02-17 15:40:45 +00:00
: returns : path to IOURC
"""
2016-06-07 13:34:04 +00:00
iourc_path = self . _config ( ) . get ( " iourc_path " )
2015-03-12 00:59:57 +00:00
if not iourc_path :
2016-11-21 17:16:50 +00:00
# look for the iourc file in the temporary dir.
path = os . path . join ( self . temporary_directory , " iourc " )
if os . path . exists ( path ) :
return path
2015-03-12 00:59:57 +00:00
# look for the iourc file in the user home dir.
path = os . path . join ( os . path . expanduser ( " ~/ " ) , " .iourc " )
if os . path . exists ( path ) :
return path
# look for the iourc file in the current working dir.
path = os . path . join ( self . working_dir , " iourc " )
if os . path . exists ( path ) :
return path
return iourc_path
2015-02-17 15:40:45 +00:00
2015-02-12 20:02:52 +00:00
@property
def ram ( self ) :
"""
2015-04-08 17:17:34 +00:00
Returns the amount of RAM allocated to this IOU VM .
: returns : amount of RAM in MBytes ( integer )
2015-02-12 20:02:52 +00:00
"""
return self . _ram
@ram.setter
def ram ( self , ram ) :
"""
Sets amount of RAM allocated to this IOU instance .
2015-04-08 17:17:34 +00:00
: param ram : amount of RAM in MBytes ( integer )
2015-02-12 20:02:52 +00:00
"""
if self . _ram == ram :
return
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: RAM updated from {old_ram} MB to {new_ram} MB ' . format ( name = self . _name ,
id = self . _id ,
old_ram = self . _ram ,
new_ram = ram ) )
2015-02-12 20:02:52 +00:00
self . _ram = ram
@property
def nvram ( self ) :
"""
Returns the mount of NVRAM allocated to this IOU instance .
2015-04-08 17:17:34 +00:00
: returns : amount of NVRAM in KBytes ( integer )
2015-02-12 20:02:52 +00:00
"""
return self . _nvram
@nvram.setter
def nvram ( self , nvram ) :
"""
Sets amount of NVRAM allocated to this IOU instance .
2015-04-08 17:17:34 +00:00
: param nvram : amount of NVRAM in KBytes ( integer )
2015-02-12 20:02:52 +00:00
"""
if self . _nvram == nvram :
return
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: NVRAM updated from {old_nvram} KB to {new_nvram} KB ' . format ( name = self . _name ,
id = self . _id ,
old_nvram = self . _nvram ,
new_nvram = nvram ) )
2015-02-12 20:02:52 +00:00
self . _nvram = nvram
2016-05-11 17:35:36 +00:00
@BaseNode.name.setter
2015-02-13 21:16:43 +00:00
def name ( self , new_name ) :
"""
2015-04-08 17:17:34 +00:00
Sets the name of this IOU VM .
2015-02-13 21:16:43 +00:00
: param new_name : name
"""
2015-06-06 21:15:03 +00:00
if self . startup_config_file :
content = self . startup_config_content
2020-01-07 17:24:47 +00:00
content = re . sub ( r " hostname .+$ " , " hostname " + new_name , content , flags = re . MULTILINE )
2015-06-06 21:15:03 +00:00
self . startup_config_content = content
2015-02-13 21:16:43 +00:00
super ( IOUVM , IOUVM ) . name . __set__ ( self , new_name )
2015-03-17 15:31:45 +00:00
@property
def iourc_content ( self ) :
2015-04-08 17:17:34 +00:00
2015-03-17 21:18:55 +00:00
try :
2015-04-25 17:58:34 +00:00
with open ( os . path . join ( self . temporary_directory , " iourc " ) , " rb " ) as f :
return f . read ( ) . decode ( " utf-8 " )
2015-03-17 21:18:55 +00:00
except OSError :
return None
2015-03-17 15:31:45 +00:00
@iourc_content.setter
def iourc_content ( self , value ) :
2015-04-08 17:17:34 +00:00
2019-05-20 03:51:24 +00:00
if value :
2016-11-11 15:18:39 +00:00
# If we don't save the value in the ~/ the licence is lost at project
# reload
path = os . path . join ( os . path . expanduser ( " ~/ " ) , " .iourc " )
try :
2018-03-12 06:38:50 +00:00
with open ( path , " wb " ) as f :
2016-11-11 15:18:39 +00:00
f . write ( value . encode ( " utf-8 " ) )
except OSError as e :
raise IOUError ( " Could not write the iourc file {} : {} " . format ( path , e ) )
2015-03-17 21:18:55 +00:00
path = os . path . join ( self . temporary_directory , " iourc " )
try :
2018-03-12 06:38:50 +00:00
with open ( path , " wb " ) as f :
2015-04-25 17:58:34 +00:00
f . write ( value . encode ( " utf-8 " ) )
2015-03-17 21:18:55 +00:00
except OSError as e :
2015-04-08 17:17:34 +00:00
raise IOUError ( " Could not write the iourc file {} : {} " . format ( path , e ) )
2015-03-17 15:31:45 +00:00
2018-11-14 08:24:30 +00:00
@property
def license_check ( self ) :
return self . _license_check
@license_check.setter
def license_check ( self , value ) :
self . _license_check = value
2018-10-15 10:05:49 +00:00
async def _library_check ( self ) :
2015-02-11 13:31:21 +00:00
"""
Checks for missing shared library dependencies in the IOU image .
"""
try :
2018-10-15 10:05:49 +00:00
output = await gns3server . utils . asyncio . subprocess_check_output ( " ldd " , self . _path )
2018-09-11 13:06:01 +00:00
except ( OSError , subprocess . SubprocessError ) as e :
2018-03-15 07:17:39 +00:00
log . warning ( " Could not determine the shared library dependencies for {} : {} " . format ( self . _path , e ) )
2015-02-11 13:31:21 +00:00
return
2019-01-17 11:01:58 +00:00
p = re . compile ( r " ([ \ . \ w]+) \ s=> \ s+not found " )
2015-02-16 16:20:07 +00:00
missing_libs = p . findall ( output )
2015-02-11 13:31:21 +00:00
if missing_libs :
raise IOUError ( " The following shared library dependencies cannot be found for IOU image {} : {} " . format ( self . _path ,
" , " . join ( missing_libs ) ) )
2018-10-15 10:05:49 +00:00
async def _check_iou_licence ( self ) :
2015-03-12 00:59:57 +00:00
"""
Checks for a valid IOU key in the iourc file ( paranoid mode ) .
"""
2018-11-14 08:24:30 +00:00
# license check is sent by the controller
if self . license_check is False :
return
2017-07-20 14:19:20 +00:00
try :
2018-11-14 08:24:30 +00:00
# we allow license check to be disabled server wide
server_wide_license_check = self . _config ( ) . getboolean ( " license_check " , True )
2017-07-20 14:19:20 +00:00
except ValueError :
raise IOUError ( " Invalid licence check setting " )
2018-11-14 08:24:30 +00:00
if server_wide_license_check is False :
2019-05-20 03:51:24 +00:00
log . warning ( " License check is explicitly disabled on this server " )
2015-03-14 19:16:27 +00:00
return
2015-03-12 00:59:57 +00:00
config = configparser . ConfigParser ( )
try :
2019-03-25 08:57:36 +00:00
log . info ( " Checking IOU license in ' {} ' " . format ( self . iourc_path ) )
2015-04-25 17:58:34 +00:00
with open ( self . iourc_path , encoding = " utf-8 " ) as f :
2015-03-12 00:59:57 +00:00
config . read_file ( f )
except OSError as e :
raise IOUError ( " Could not open iourc file {} : {} " . format ( self . iourc_path , e ) )
except configparser . Error as e :
raise IOUError ( " Could not parse iourc file {} : {} " . format ( self . iourc_path , e ) )
2015-04-27 03:15:15 +00:00
except UnicodeDecodeError as e :
2015-09-22 11:45:10 +00:00
raise IOUError ( " Non ascii characters in iourc file {} , please remove them: {} " . format ( self . iourc_path , e ) )
2015-03-12 00:59:57 +00:00
if " license " not in config :
raise IOUError ( " License section not found in iourc file {} " . format ( self . iourc_path ) )
hostname = socket . gethostname ( )
2017-10-24 08:07:32 +00:00
if len ( hostname ) > 15 :
log . warning ( " Older IOU images may not boot because hostname ' {} ' length is above 15 characters " . format ( hostname ) )
2015-03-12 00:59:57 +00:00
if hostname not in config [ " license " ] :
2016-01-13 15:37:50 +00:00
raise IOUError ( " Hostname \" {} \" not found in iourc file {} " . format ( hostname , self . iourc_path ) )
2015-03-12 00:59:57 +00:00
user_ioukey = config [ " license " ] [ hostname ]
if user_ioukey [ - 1 : ] != ' ; ' :
2016-02-05 09:06:34 +00:00
raise IOUError ( " IOU key not ending with ; in iourc file {} " . format ( self . iourc_path ) )
2015-03-12 00:59:57 +00:00
if len ( user_ioukey ) != 17 :
2016-02-05 09:06:34 +00:00
raise IOUError ( " IOU key length is not 16 characters in iourc file {} " . format ( self . iourc_path ) )
2015-03-12 00:59:57 +00:00
user_ioukey = user_ioukey [ : 16 ]
2015-03-16 10:52:22 +00:00
# We can't test this because it's mean distributing a valid licence key
# in tests or generating one
2015-03-17 18:00:14 +00:00
if not hasattr ( sys , " _called_from_test " ) :
2015-03-16 10:52:22 +00:00
try :
2018-10-15 10:05:49 +00:00
hostid = ( await gns3server . utils . asyncio . subprocess_check_output ( " hostid " ) ) . strip ( )
2015-03-16 10:52:22 +00:00
except FileNotFoundError as e :
raise IOUError ( " Could not find hostid: {} " . format ( e ) )
2018-09-11 13:06:01 +00:00
except ( OSError , subprocess . SubprocessError ) as e :
2015-03-16 10:52:22 +00:00
raise IOUError ( " Could not execute hostid: {} " . format ( e ) )
try :
ioukey = int ( hostid , 16 )
except ValueError :
raise IOUError ( " Invalid hostid detected: {} " . format ( hostid ) )
for x in hostname :
ioukey + = ord ( x )
pad1 = b ' \x4B \x58 \x21 \x81 \x56 \x7B \x0D \xF3 \x21 \x43 \x9B \x7E \xAC \x1D \xE6 \x8A '
pad2 = b ' \x80 ' + 39 * b ' \0 '
2015-03-17 19:15:01 +00:00
ioukey = hashlib . md5 ( pad1 + pad2 + struct . pack ( ' !I ' , ioukey ) + pad1 ) . hexdigest ( ) [ : 16 ]
2015-03-16 10:52:22 +00:00
if ioukey != user_ioukey :
raise IOUError ( " Invalid IOU license key {} detected in iourc file {} for host {} " . format ( user_ioukey ,
self . iourc_path ,
hostname ) )
2015-03-12 00:59:57 +00:00
2016-06-10 16:26:01 +00:00
def _nvram_file ( self ) :
"""
Path to the nvram file
"""
return os . path . join ( self . working_dir , " nvram_ {:05d} " . format ( self . application_id ) )
2015-06-06 21:15:03 +00:00
def _push_configs_to_nvram ( self ) :
"""
Push the startup - config and private - config content to the NVRAM .
"""
startup_config_content = self . startup_config_content
if startup_config_content :
2016-06-10 16:26:01 +00:00
nvram_file = self . _nvram_file ( )
2015-06-06 21:15:03 +00:00
try :
if not os . path . exists ( nvram_file ) :
open ( nvram_file , " a " ) . close ( )
2015-06-08 16:07:54 +00:00
nvram_content = None
else :
with open ( nvram_file , " rb " ) as file :
nvram_content = file . read ( )
2015-06-06 21:15:03 +00:00
except OSError as e :
raise IOUError ( " Cannot read nvram file {} : {} " . format ( nvram_file , e ) )
startup_config_content = startup_config_content . encode ( " utf-8 " )
private_config_content = self . private_config_content
if private_config_content is not None :
private_config_content = private_config_content . encode ( " utf-8 " )
try :
nvram_content = nvram_import ( nvram_content , startup_config_content , private_config_content , self . nvram )
except ValueError as e :
raise IOUError ( " Cannot push configs to nvram {} : {} " . format ( nvram_file , e ) )
try :
with open ( nvram_file , " wb " ) as file :
file . write ( nvram_content )
except OSError as e :
raise IOUError ( " Cannot write nvram file {} : {} " . format ( nvram_file , e ) )
2018-10-15 10:05:49 +00:00
async def start ( self ) :
2015-02-11 13:31:21 +00:00
"""
Starts the IOU process .
"""
self . _check_requirements ( )
if not self . is_running ( ) :
2018-10-15 10:05:49 +00:00
await self . _library_check ( )
2015-02-13 19:57:20 +00:00
2015-05-13 08:16:24 +00:00
try :
self . _rename_nvram_file ( )
except OSError as e :
raise IOUError ( " Could not rename nvram files: {} " . format ( e ) )
2015-02-11 13:31:21 +00:00
2015-02-17 15:40:45 +00:00
iourc_path = self . iourc_path
2015-05-13 22:05:54 +00:00
if not iourc_path :
2019-05-20 03:51:24 +00:00
raise IOUError ( " Could not find an iourc file (IOU license), please configure an IOU license " )
2015-03-26 21:11:36 +00:00
if not os . path . isfile ( iourc_path ) :
raise IOUError ( " The iourc path ' {} ' is not a regular file " . format ( iourc_path ) )
2015-02-11 13:31:21 +00:00
2018-10-15 10:05:49 +00:00
await self . _check_iou_licence ( )
await self . _start_ubridge ( )
2015-02-11 13:31:21 +00:00
self . _create_netmap_config ( )
2017-11-16 09:52:19 +00:00
if self . use_default_iou_values :
# make sure we have the default nvram amount to correctly push the configs
2018-10-15 10:05:49 +00:00
await self . update_default_iou_values ( )
2015-06-06 21:15:03 +00:00
self . _push_configs_to_nvram ( )
2015-10-12 21:57:37 +00:00
# check if there is enough RAM to run
self . check_available_ram ( self . ram )
2017-01-10 14:50:35 +00:00
self . _nvram_watcher = FileWatcher ( self . _nvram_file ( ) , self . _nvram_changed , delay = 2 )
2016-06-10 16:26:01 +00:00
2015-02-11 13:31:21 +00:00
# created a environment variable pointing to the iourc file.
env = os . environ . copy ( )
2019-05-20 03:51:24 +00:00
if " IOURC " not in os . environ and iourc_path :
2015-02-17 15:40:45 +00:00
env [ " IOURC " ] = iourc_path
2018-12-18 04:42:18 +00:00
# create a symbolic link to the image to avoid IOU error "failed code signing checks"
# on newer images, see https://github.com/GNS3/gns3-server/issues/1484
try :
2023-10-26 05:05:10 +00:00
iou_image_path = os . path . basename ( self . path )
if len ( iou_image_path ) > 63 :
# IOU file basename length must be <= 63 chars
iou_file_name , iou_file_ext = os . path . splitext ( iou_image_path )
iou_image_path = iou_file_name [ : 63 - len ( iou_file_ext ) ] + iou_file_ext
symlink = os . path . join ( self . working_dir , iou_image_path )
2019-02-18 16:09:59 +00:00
if os . path . islink ( symlink ) :
os . unlink ( symlink )
os . symlink ( self . path , symlink )
2018-12-18 04:42:18 +00:00
except OSError as e :
raise IOUError ( " Could not create symbolic link: {} " . format ( e ) )
2018-10-15 10:05:49 +00:00
command = await self . _build_command ( )
2015-02-11 13:31:21 +00:00
try :
2016-02-02 17:25:17 +00:00
log . info ( " Starting IOU: {} " . format ( command ) )
2016-11-08 09:21:20 +00:00
self . command_line = ' ' . join ( command )
2018-10-15 10:05:49 +00:00
self . _iou_process = await asyncio . create_subprocess_exec (
2016-11-08 09:21:20 +00:00
* command ,
stdout = asyncio . subprocess . PIPE ,
stdin = asyncio . subprocess . PIPE ,
stderr = subprocess . STDOUT ,
cwd = self . working_dir ,
env = env )
2015-02-11 13:31:21 +00:00
log . info ( " IOU instance {} started PID= {} " . format ( self . _id , self . _iou_process . pid ) )
self . _started = True
2015-03-04 15:01:56 +00:00
self . status = " started "
2015-12-06 01:24:08 +00:00
callback = functools . partial ( self . _termination_callback , " IOU " )
gns3server . utils . asyncio . monitor_process ( self . _iou_process , callback )
2015-02-11 13:31:21 +00:00
except FileNotFoundError as e :
2015-04-08 17:17:34 +00:00
raise IOUError ( " Could not start IOU: {} : 32-bit binary support is probably not installed " . format ( e ) )
2015-02-11 13:31:21 +00:00
except ( OSError , subprocess . SubprocessError ) as e :
iou_stdout = self . read_iou_stdout ( )
2015-04-08 17:17:34 +00:00
log . error ( " Could not start IOU {} : {} \n {} " . format ( self . _path , e , iou_stdout ) )
raise IOUError ( " Could not start IOU {} : {} \n {} " . format ( self . _path , e , iou_stdout ) )
2015-02-11 13:31:21 +00:00
2020-07-26 08:57:18 +00:00
await self . start_console ( )
2016-11-06 10:27:49 +00:00
# configure networking support
2018-10-15 10:05:49 +00:00
await self . _networking ( )
2016-11-06 10:27:49 +00:00
2020-07-26 08:57:18 +00:00
async def start_console ( self ) :
"""
Start the Telnet server to provide console access .
"""
if self . console and self . console_type == " telnet " :
server = AsyncioTelnetServer ( reader = self . _iou_process . stdout , writer = self . _iou_process . stdin , binary = True ,
echo = True )
try :
self . _telnet_server = await asyncio . start_server ( server . run , self . _manager . port_manager . console_host ,
self . console )
except OSError as e :
await self . stop ( )
raise IOUError (
" Could not start Telnet server on socket {} : {} : {} " . format ( self . _manager . port_manager . console_host ,
self . console , e ) )
async def reset_console ( self ) :
"""
Reset the console .
"""
if self . _telnet_server :
self . _telnet_server . close ( )
await self . _telnet_server . wait_closed ( )
self . _telnet_server = None
await self . start_console ( )
2018-08-25 07:10:47 +00:00
@locking
2018-10-15 10:05:49 +00:00
async def _networking ( self ) :
2016-11-06 10:27:49 +00:00
"""
Configures the IOL bridge in uBridge .
"""
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
try :
# delete any previous bridge if it exists
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge delete {name} " . format ( name = bridge_name ) )
2016-11-06 10:27:49 +00:00
except UbridgeError :
pass
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge create {name} {bridge_id} " . format ( name = bridge_name , bridge_id = self . application_id + 512 ) )
2016-11-06 10:27:49 +00:00
bay_id = 0
for adapter in self . _adapters :
unit_id = 0
for unit in adapter . ports . keys ( ) :
nio = adapter . get_nio ( unit )
if nio and isinstance ( nio , NIOUDP ) :
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport} " . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
iol_id = self . application_id ,
bay = bay_id ,
unit = unit_id ,
lport = nio . lport ,
rhost = nio . rhost ,
rport = nio . rport ) )
if nio . capturing :
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( ' iol_bridge start_capture {name} " {output_file} " {data_link_type} ' . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
output_file = nio . pcap_output_file ,
2019-01-17 11:01:58 +00:00
data_link_type = re . sub ( r " ^DLT_ " , " " , nio . pcap_data_link_type ) ) )
2016-11-06 10:27:49 +00:00
2018-10-15 10:05:49 +00:00
await self . _ubridge_apply_filters ( bay_id , unit_id , nio . filters )
2016-11-06 10:27:49 +00:00
unit_id + = 1
bay_id + = 1
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge start {name} " . format ( name = bridge_name ) )
2015-02-11 16:11:18 +00:00
2015-12-06 01:24:08 +00:00
def _termination_callback ( self , process_name , returncode ) :
2015-03-04 15:01:56 +00:00
"""
2015-05-13 19:53:42 +00:00
Called when the process has stopped .
2015-03-04 15:01:56 +00:00
: param returncode : Process returncode
"""
2015-05-13 19:53:42 +00:00
2015-03-04 15:01:56 +00:00
self . _terminate_process_iou ( )
2015-07-04 20:08:03 +00:00
if returncode != 0 :
2017-03-20 08:46:07 +00:00
if returncode == - 11 :
2017-10-22 09:28:54 +00:00
message = ' IOU VM " {} " process has stopped with return code: {} (segfault). This could be an issue with the IOU image, using a different image may fix this. \n {} ' . format ( self . name ,
returncode ,
self . read_iou_stdout ( ) )
2016-10-28 09:25:14 +00:00
else :
2017-10-22 09:28:54 +00:00
message = ' IOU VM " {} " process has stopped with return code: {} \n {} ' . format ( self . name , returncode , self . read_iou_stdout ( ) )
log . warning ( message )
2016-10-28 09:25:14 +00:00
self . project . emit ( " log.error " , { " message " : message } )
2016-11-08 09:21:20 +00:00
if self . _telnet_server :
self . _telnet_server . close ( )
self . _telnet_server = None
2015-03-04 15:01:56 +00:00
2015-02-13 19:57:20 +00:00
def _rename_nvram_file ( self ) :
"""
2015-04-08 17:17:34 +00:00
Before starting the VM , rename the nvram and vlan . dat files with the correct IOU application identifier .
2015-02-13 19:57:20 +00:00
"""
2016-06-10 16:26:01 +00:00
destination = self . _nvram_file ( )
2015-10-07 14:44:50 +00:00
for file_path in glob . glob ( os . path . join ( glob . escape ( self . working_dir ) , " nvram_* " ) ) :
2015-02-13 19:57:20 +00:00
shutil . move ( file_path , destination )
2015-03-05 16:00:25 +00:00
destination = os . path . join ( self . working_dir , " vlan.dat- {:05d} " . format ( self . application_id ) )
2015-10-07 14:44:50 +00:00
for file_path in glob . glob ( os . path . join ( glob . escape ( self . working_dir ) , " vlan.dat-* " ) ) :
2015-03-05 16:00:25 +00:00
shutil . move ( file_path , destination )
2015-02-13 19:57:20 +00:00
2018-10-15 10:05:49 +00:00
async def stop ( self ) :
2015-02-11 13:31:21 +00:00
"""
Stops the IOU process .
"""
2018-10-15 10:05:49 +00:00
await self . _stop_ubridge ( )
2016-06-10 16:26:01 +00:00
if self . _nvram_watcher :
self . _nvram_watcher . close ( )
self . _nvram_watcher = None
2016-11-08 09:21:20 +00:00
if self . _telnet_server :
self . _telnet_server . close ( )
self . _telnet_server = None
2015-02-16 16:40:13 +00:00
2016-11-08 09:21:20 +00:00
if self . is_running ( ) :
2015-02-11 16:11:18 +00:00
self . _terminate_process_iou ( )
2015-02-26 10:29:57 +00:00
if self . _iou_process . returncode is None :
try :
2018-10-15 10:05:49 +00:00
await gns3server . utils . asyncio . wait_for_process_termination ( self . _iou_process , timeout = 3 )
2015-02-26 10:29:57 +00:00
except asyncio . TimeoutError :
if self . _iou_process . returncode is None :
2017-10-22 09:28:54 +00:00
log . warning ( " IOU process {} is still running... killing it " . format ( self . _iou_process . pid ) )
2016-07-04 12:46:06 +00:00
try :
self . _iou_process . kill ( )
except ProcessLookupError :
pass
2015-02-11 13:31:21 +00:00
self . _iou_process = None
2015-02-11 16:11:18 +00:00
2019-03-18 11:05:40 +00:00
try :
symlink = os . path . join ( self . working_dir , os . path . basename ( self . path ) )
if os . path . islink ( symlink ) :
os . unlink ( symlink )
except OSError as e :
log . warning ( " Could not delete symbolic link: {} " . format ( e ) )
2015-03-25 04:04:48 +00:00
self . _started = False
2016-05-22 00:58:28 +00:00
self . save_configs ( )
2015-02-11 13:31:21 +00:00
2015-02-11 16:11:18 +00:00
def _terminate_process_iou ( self ) :
2015-04-08 17:17:34 +00:00
"""
Terminate the IOU process if running
"""
2015-02-11 13:31:21 +00:00
2015-03-04 15:01:56 +00:00
if self . _iou_process :
log . info ( ' Stopping IOU process for IOU VM " {} " PID= {} ' . format ( self . name , self . _iou_process . pid ) )
try :
self . _iou_process . terminate ( )
# Sometime the process can already be dead when we garbage collect
except ProcessLookupError :
pass
self . _started = False
self . status = " stopped "
2015-02-11 13:31:21 +00:00
2018-10-15 10:05:49 +00:00
async def reload ( self ) :
2015-02-11 13:31:21 +00:00
"""
2015-04-08 17:17:34 +00:00
Reloads the IOU process ( stop & start ) .
2015-02-11 13:31:21 +00:00
"""
2018-10-15 10:05:49 +00:00
await self . stop ( )
await self . start ( )
2015-02-11 13:31:21 +00:00
def is_running ( self ) :
"""
Checks if the IOU process is running
: returns : True or False
"""
2015-03-25 04:04:48 +00:00
if self . _iou_process and self . _iou_process . returncode is None :
2015-02-11 13:31:21 +00:00
return True
return False
2018-03-24 11:11:21 +00:00
@BaseNode.console_type.setter
def console_type ( self , new_console_type ) :
"""
Sets the console type for this IOU VM .
: param new_console_type : console type ( string )
"""
if self . is_running ( ) and self . console_type != new_console_type :
raise IOUError ( ' " {name} " must be stopped to change the console type to {new_console_type} ' . format ( name = self . _name , new_console_type = new_console_type ) )
super ( IOUVM , IOUVM ) . console_type . __set__ ( self , new_console_type )
2015-02-11 13:31:21 +00:00
def _create_netmap_config ( self ) :
"""
Creates the NETMAP file .
"""
netmap_path = os . path . join ( self . working_dir , " NETMAP " )
try :
2015-04-25 17:58:34 +00:00
with open ( netmap_path , " w " , encoding = " utf-8 " ) as f :
2015-02-11 13:31:21 +00:00
for bay in range ( 0 , 16 ) :
for unit in range ( 0 , 4 ) :
2016-11-06 10:27:49 +00:00
f . write ( " {ubridge_id} : {bay} / {unit} {iou_id:>5d} : {bay} / {unit} \n " . format ( ubridge_id = str ( self . application_id + 512 ) ,
bay = bay ,
unit = unit ,
iou_id = self . application_id ) )
2015-02-11 13:31:21 +00:00
log . info ( " IOU {name} [id= {id} ]: NETMAP file created " . format ( name = self . _name ,
id = self . _id ) )
except OSError as e :
raise IOUError ( " Could not create {} : {} " . format ( netmap_path , e ) )
2018-10-15 10:05:49 +00:00
async def _build_command ( self ) :
2015-02-11 13:31:21 +00:00
"""
Command to start the IOU process .
( to be passed to subprocess . Popen ( ) )
2015-04-08 17:17:34 +00:00
2015-02-11 13:31:21 +00:00
IOU command line :
Usage : < image > [ options ] < application id >
< image > : unix - js - m | unix - is - m | unix - i - m | . . .
< application id > : instance identifier ( 0 < id < = 1024 )
Options :
- e < n > Number of Ethernet interfaces ( default 2 )
- s < n > Number of Serial interfaces ( default 2 )
- n < n > Size of nvram in Kb ( default 64 KB )
- b < string > IOS debug string
- c < name > Configuration file name
- d Generate debug information
- t Netio message trace
- q Suppress informational messages
- h Display this help
- C Turn off use of host clock
- m < n > Megabytes of router memory ( default 256 MB )
- L Disable local console , use remote console
- l Enable Layer 1 keepalive messages
- u < n > UDP port base for distributed networks
- R Ignore options from the IOURC file
- U Disable unix : file system location
- W Disable watchdog timer
- N Ignore the NETMAP file
"""
2015-02-12 14:20:47 +00:00
command = [ self . _path ]
2015-02-11 13:31:21 +00:00
if len ( self . _ethernet_adapters ) != 2 :
command . extend ( [ " -e " , str ( len ( self . _ethernet_adapters ) ) ] )
if len ( self . _serial_adapters ) != 2 :
command . extend ( [ " -s " , str ( len ( self . _serial_adapters ) ) ] )
if not self . use_default_iou_values :
command . extend ( [ " -n " , str ( self . _nvram ) ] )
command . extend ( [ " -m " , str ( self . _ram ) ] )
2015-02-13 21:16:43 +00:00
2015-06-06 21:15:03 +00:00
# do not let IOU create the NVRAM anymore
#startup_config_file = self.startup_config_file
2015-06-17 15:11:25 +00:00
# if startup_config_file:
2015-06-06 21:15:03 +00:00
# command.extend(["-c", os.path.basename(startup_config_file)])
2015-02-11 13:31:21 +00:00
if self . _l1_keepalives :
2018-10-15 10:05:49 +00:00
await self . _enable_l1_keepalives ( command )
2015-02-11 13:31:21 +00:00
command . extend ( [ str ( self . application_id ) ] )
return command
def read_iou_stdout ( self ) :
"""
Reads the standard output of the IOU process .
Only use when the process has been stopped or has crashed .
"""
output = " "
if self . _iou_stdout_file :
try :
2015-04-25 17:58:34 +00:00
with open ( self . _iou_stdout_file , " rb " ) as file :
output = file . read ( ) . decode ( " utf-8 " , errors = " replace " )
2015-02-11 13:31:21 +00:00
except OSError as e :
2018-03-15 07:17:39 +00:00
log . warning ( " could not read {} : {} " . format ( self . _iou_stdout_file , e ) )
2015-02-11 13:31:21 +00:00
return output
2017-07-17 09:21:54 +00:00
@property
def adapters ( self ) :
return self . _adapters
2015-02-12 20:02:52 +00:00
@property
def ethernet_adapters ( self ) :
"""
2015-04-08 17:17:34 +00:00
Returns the number of Ethernet adapters for this IOU VM .
2015-02-12 20:02:52 +00:00
: returns : number of adapters
"""
return len ( self . _ethernet_adapters )
@ethernet_adapters.setter
def ethernet_adapters ( self , ethernet_adapters ) :
"""
2015-04-08 17:17:34 +00:00
Sets the number of Ethernet adapters for this IOU VM .
2015-02-12 20:02:52 +00:00
: param ethernet_adapters : number of adapters
"""
self . _ethernet_adapters . clear ( )
for _ in range ( 0 , ethernet_adapters ) :
2015-02-13 19:57:20 +00:00
self . _ethernet_adapters . append ( EthernetAdapter ( interfaces = 4 ) )
2015-02-12 20:02:52 +00:00
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: number of Ethernet adapters changed to {adapters} ' . format ( name = self . _name ,
id = self . _id ,
adapters = len ( self . _ethernet_adapters ) ) )
2015-02-12 20:02:52 +00:00
2015-02-16 19:08:04 +00:00
self . _adapters = self . _ethernet_adapters + self . _serial_adapters
2015-02-12 20:02:52 +00:00
@property
def serial_adapters ( self ) :
"""
2015-04-08 17:17:34 +00:00
Returns the number of Serial adapters for this IOU VM .
2015-02-12 20:02:52 +00:00
: returns : number of adapters
"""
return len ( self . _serial_adapters )
@serial_adapters.setter
def serial_adapters ( self , serial_adapters ) :
"""
2015-04-08 17:17:34 +00:00
Sets the number of Serial adapters for this IOU VM .
2015-02-12 20:02:52 +00:00
: param serial_adapters : number of adapters
"""
self . _serial_adapters . clear ( )
for _ in range ( 0 , serial_adapters ) :
2015-02-13 19:57:20 +00:00
self . _serial_adapters . append ( SerialAdapter ( interfaces = 4 ) )
2015-02-12 20:02:52 +00:00
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: number of Serial adapters changed to {adapters} ' . format ( name = self . _name ,
id = self . _id ,
adapters = len ( self . _serial_adapters ) ) )
2015-02-12 20:02:52 +00:00
2015-02-16 19:08:04 +00:00
self . _adapters = self . _ethernet_adapters + self . _serial_adapters
2015-02-12 21:28:12 +00:00
2018-10-15 10:05:49 +00:00
async def adapter_add_nio_binding ( self , adapter_number , port_number , nio ) :
2015-02-12 21:28:12 +00:00
"""
2018-10-27 07:47:17 +00:00
Adds an adapter NIO binding .
2015-04-08 17:17:34 +00:00
: param adapter_number : adapter number
: param port_number : port number
2015-02-16 19:08:04 +00:00
: param nio : NIO instance to add to the adapter / port
2015-02-12 21:28:12 +00:00
"""
try :
2015-02-16 19:08:04 +00:00
adapter = self . _adapters [ adapter_number ]
2015-02-12 21:28:12 +00:00
except IndexError :
2015-04-08 17:17:34 +00:00
raise IOUError ( ' Adapter {adapter_number} does not exist for IOU " {name} " ' . format ( name = self . _name ,
adapter_number = adapter_number ) )
2015-02-16 09:18:03 +00:00
if not adapter . port_exists ( port_number ) :
2018-10-27 07:47:17 +00:00
raise IOUError ( " Port {port_number} does not exist on adapter {adapter} " . format ( adapter = adapter ,
2015-04-08 17:17:34 +00:00
port_number = port_number ) )
2015-02-16 09:18:03 +00:00
adapter . add_nio ( port_number , nio )
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: {nio} added to {adapter_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio = nio ,
adapter_number = adapter_number ,
port_number = port_number ) )
2016-11-06 10:27:49 +00:00
2016-12-14 11:01:34 +00:00
if self . ubridge :
2016-11-06 10:27:49 +00:00
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport} " . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
iol_id = self . application_id ,
bay = adapter_number ,
unit = port_number ,
lport = nio . lport ,
rhost = nio . rhost ,
rport = nio . rport ) )
2018-10-15 10:05:49 +00:00
await self . _ubridge_apply_filters ( adapter_number , port_number , nio . filters )
2017-07-17 09:21:54 +00:00
2018-10-15 10:05:49 +00:00
async def adapter_update_nio_binding ( self , adapter_number , port_number , nio ) :
2017-07-17 09:21:54 +00:00
"""
2018-10-27 07:47:17 +00:00
Updates an adapter NIO binding .
2017-07-17 09:21:54 +00:00
: param adapter_number : adapter number
: param port_number : port number
: param nio : NIO instance to add to the adapter
"""
if self . ubridge :
2018-10-15 10:05:49 +00:00
await self . _ubridge_apply_filters ( adapter_number , port_number , nio . filters )
2017-07-17 09:21:54 +00:00
2018-10-15 10:05:49 +00:00
async def _ubridge_apply_filters ( self , adapter_number , port_number , filters ) :
2017-07-17 09:21:54 +00:00
"""
Apply filter like rate limiting
: param adapter_number : adapter number
: param port_number : port number
: param filters : Array of filter dictionnary
"""
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
location = ' {bridge_name} {bay} {unit} ' . format (
bridge_name = bridge_name ,
bay = adapter_number ,
unit = port_number )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( ' iol_bridge reset_packet_filters ' + location )
2017-07-17 09:21:54 +00:00
for filter in self . _build_filter_list ( filters ) :
cmd = ' iol_bridge add_packet_filter {} {} ' . format (
location ,
filter )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( cmd )
2015-02-12 21:28:12 +00:00
2018-10-15 10:05:49 +00:00
async def adapter_remove_nio_binding ( self , adapter_number , port_number ) :
2015-02-12 21:28:12 +00:00
"""
2015-04-08 17:17:34 +00:00
Removes an adapter NIO binding .
: param adapter_number : adapter number
: param port_number : port number
2018-10-27 07:47:17 +00:00
2015-02-12 21:28:12 +00:00
: returns : NIO instance
"""
try :
2015-02-16 19:08:04 +00:00
adapter = self . _adapters [ adapter_number ]
2015-02-12 21:28:12 +00:00
except IndexError :
2015-04-08 17:17:34 +00:00
raise IOUError ( ' Adapter {adapter_number} does not exist on IOU " {name} " ' . format ( name = self . _name ,
adapter_number = adapter_number ) )
2015-02-16 09:18:03 +00:00
if not adapter . port_exists ( port_number ) :
2018-10-27 07:47:17 +00:00
raise IOUError ( " Port {port_number} does not exist on adapter {adapter} " . format ( adapter = adapter ,
2015-04-08 17:17:34 +00:00
port_number = port_number ) )
2015-02-16 09:18:03 +00:00
nio = adapter . get_nio ( port_number )
2015-02-24 02:00:34 +00:00
if isinstance ( nio , NIOUDP ) :
2015-03-21 23:19:12 +00:00
self . manager . port_manager . release_udp_port ( nio . lport , self . _project )
2015-02-16 09:18:03 +00:00
adapter . remove_nio ( port_number )
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: {nio} removed from {adapter_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio = nio ,
adapter_number = adapter_number ,
port_number = port_number ) )
2016-11-06 10:27:49 +00:00
2016-12-14 11:01:34 +00:00
if self . ubridge :
2016-11-06 10:27:49 +00:00
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( " iol_bridge delete_nio_udp {name} {bay} {unit} " . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
bay = adapter_number ,
unit = port_number ) )
2015-02-12 21:28:12 +00:00
return nio
2015-02-13 15:57:35 +00:00
2018-10-27 07:47:17 +00:00
def get_nio ( self , adapter_number , port_number ) :
"""
Gets an adapter NIO binding .
: param adapter_number : adapter number
: param port_number : port number
: returns : NIO instance
"""
try :
adapter = self . _adapters [ adapter_number ]
except IndexError :
raise IOUError ( ' Adapter {adapter_number} does not exist on IOU " {name} " ' . format ( name = self . _name ,
adapter_number = adapter_number ) )
if not adapter . port_exists ( port_number ) :
raise IOUError ( " Port {port_number} does not exist on adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
nio = adapter . get_nio ( port_number )
if not nio :
raise IOUError ( " NIO {port_number} does not exist on adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
return nio
2015-02-13 15:57:35 +00:00
@property
def l1_keepalives ( self ) :
"""
Returns either layer 1 keepalive messages option is enabled or disabled .
2015-04-08 17:17:34 +00:00
2015-02-13 15:57:35 +00:00
: returns : boolean
"""
return self . _l1_keepalives
@l1_keepalives.setter
def l1_keepalives ( self , state ) :
"""
Enables or disables layer 1 keepalive messages .
2015-04-08 17:17:34 +00:00
2015-02-13 15:57:35 +00:00
: param state : boolean
"""
self . _l1_keepalives = state
if state :
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: has activated layer 1 keepalive messages ' . format ( name = self . _name , id = self . _id ) )
2015-02-13 15:57:35 +00:00
else :
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: has deactivated layer 1 keepalive messages ' . format ( name = self . _name , id = self . _id ) )
2015-02-13 15:57:35 +00:00
2018-10-15 10:05:49 +00:00
async def _enable_l1_keepalives ( self , command ) :
2015-02-13 15:57:35 +00:00
"""
Enables L1 keepalive messages if supported .
2015-04-08 17:17:34 +00:00
2015-02-13 15:57:35 +00:00
: param command : command line
"""
env = os . environ . copy ( )
2015-03-12 00:59:57 +00:00
if " IOURC " not in os . environ :
env [ " IOURC " ] = self . iourc_path
2015-02-13 15:57:35 +00:00
try :
2018-10-15 10:05:49 +00:00
output = await gns3server . utils . asyncio . subprocess_check_output ( self . _path , " -h " , cwd = self . working_dir , env = env , stderr = True )
2019-01-17 11:01:58 +00:00
if re . search ( r " -l \ s+Enable Layer 1 keepalive messages " , output ) :
2015-02-13 15:57:35 +00:00
command . extend ( [ " -l " ] )
else :
raise IOUError ( " layer 1 keepalive messages are not supported by {} " . format ( os . path . basename ( self . _path ) ) )
except ( OSError , subprocess . SubprocessError ) as e :
2017-10-12 15:32:45 +00:00
log . warning ( " could not determine if layer 1 keepalive messages are supported by {} : {} " . format ( os . path . basename ( self . _path ) , e ) )
2015-02-13 21:16:43 +00:00
@property
2015-06-06 21:15:03 +00:00
def startup_config_content ( self ) :
"""
Returns the content of the current startup - config file .
"""
config_file = self . startup_config_file
if config_file is None :
return None
try :
with open ( config_file , " rb " ) as f :
return f . read ( ) . decode ( " utf-8 " , errors = " replace " )
except OSError as e :
raise IOUError ( " Can ' t read startup-config file ' {} ' : {} " . format ( config_file , e ) )
@startup_config_content.setter
def startup_config_content ( self , startup_config ) :
"""
Update the startup config
: param startup_config : content of the startup configuration file
"""
try :
startup_config_path = os . path . join ( self . working_dir , " startup-config.cfg " )
if startup_config is None :
startup_config = ' '
# We disallow erasing the startup config file
if len ( startup_config ) == 0 and os . path . exists ( startup_config_path ) :
return
with open ( startup_config_path , ' w+ ' , encoding = ' utf-8 ' ) as f :
if len ( startup_config ) == 0 :
f . write ( ' ' )
else :
startup_config = startup_config . replace ( " % h " , self . _name )
f . write ( startup_config )
2016-06-09 23:47:45 +00:00
vlan_file = os . path . join ( self . working_dir , " vlan.dat- {:05d} " . format ( self . application_id ) )
if os . path . exists ( vlan_file ) :
try :
os . remove ( vlan_file )
except OSError as e :
log . error ( " Could not delete VLAN file ' {} ' : {} " . format ( vlan_file , e ) )
2015-06-06 21:15:03 +00:00
except OSError as e :
raise IOUError ( " Can ' t write startup-config file ' {} ' : {} " . format ( startup_config_path , e ) )
@property
def private_config_content ( self ) :
2015-04-08 17:17:34 +00:00
"""
2015-06-06 21:15:03 +00:00
Returns the content of the current private - config file .
2015-04-08 17:17:34 +00:00
"""
2015-02-13 21:16:43 +00:00
2015-06-06 21:15:03 +00:00
config_file = self . private_config_file
2015-02-13 21:16:43 +00:00
if config_file is None :
return None
try :
2015-04-25 17:58:34 +00:00
with open ( config_file , " rb " ) as f :
return f . read ( ) . decode ( " utf-8 " , errors = " replace " )
2015-02-13 21:16:43 +00:00
except OSError as e :
2015-06-06 21:15:03 +00:00
raise IOUError ( " Can ' t read private-config file ' {} ' : {} " . format ( config_file , e ) )
2015-02-13 21:16:43 +00:00
2015-06-06 21:15:03 +00:00
@private_config_content.setter
def private_config_content ( self , private_config ) :
2015-02-13 21:16:43 +00:00
"""
2015-06-06 21:15:03 +00:00
Update the private config
2015-02-13 21:16:43 +00:00
2015-06-06 21:15:03 +00:00
: param private_config : content of the private configuration file
2015-02-13 21:16:43 +00:00
"""
try :
2015-06-06 21:15:03 +00:00
private_config_path = os . path . join ( self . working_dir , " private-config.cfg " )
2015-04-06 19:30:57 +00:00
2015-06-06 21:15:03 +00:00
if private_config is None :
private_config = ' '
2015-04-09 08:27:50 +00:00
2016-05-22 00:58:28 +00:00
# We disallow erasing the private config file
2015-06-06 21:15:03 +00:00
if len ( private_config ) == 0 and os . path . exists ( private_config_path ) :
2015-04-06 19:30:57 +00:00
return
2015-06-06 21:15:03 +00:00
with open ( private_config_path , ' w+ ' , encoding = ' utf-8 ' ) as f :
if len ( private_config ) == 0 :
2015-02-13 21:16:43 +00:00
f . write ( ' ' )
else :
2015-06-06 21:15:03 +00:00
private_config = private_config . replace ( " % h " , self . _name )
f . write ( private_config )
2015-02-13 21:16:43 +00:00
except OSError as e :
2015-06-06 21:15:03 +00:00
raise IOUError ( " Can ' t write private-config file ' {} ' : {} " . format ( private_config_path , e ) )
2015-02-13 21:16:43 +00:00
@property
2015-06-06 21:15:03 +00:00
def startup_config_file ( self ) :
2015-02-13 21:16:43 +00:00
"""
2015-06-06 21:15:03 +00:00
Returns the startup - config file for this IOU VM .
2015-02-13 21:16:43 +00:00
: returns : path to config file . None if the file doesn ' t exist
"""
2015-06-06 21:15:03 +00:00
path = os . path . join ( self . working_dir , ' startup-config.cfg ' )
2015-02-13 21:16:43 +00:00
if os . path . exists ( path ) :
return path
else :
return None
2015-02-16 19:08:04 +00:00
2015-02-17 13:52:51 +00:00
@property
2015-06-06 21:15:03 +00:00
def private_config_file ( self ) :
2015-02-17 13:52:51 +00:00
"""
2015-06-06 21:15:03 +00:00
Returns the private - config file for this IOU VM .
2015-02-17 13:52:51 +00:00
: returns : path to config file . None if the file doesn ' t exist
"""
2015-06-06 21:15:03 +00:00
path = os . path . join ( self . working_dir , ' private-config.cfg ' )
2015-02-17 13:52:51 +00:00
if os . path . exists ( path ) :
2015-06-06 21:15:03 +00:00
return path
2015-02-17 13:52:51 +00:00
else :
return None
2015-06-06 21:15:03 +00:00
@property
def relative_startup_config_file ( self ) :
"""
Returns the startup - config file relative to the project directory .
It ' s compatible with pre 1.3 projects.
: returns : path to startup - config file . None if the file doesn ' t exist
"""
path = os . path . join ( self . working_dir , ' startup-config.cfg ' )
if os . path . exists ( path ) :
return ' startup-config.cfg '
else :
return None
@property
def relative_private_config_file ( self ) :
"""
Returns the private - config file relative to the project directory .
: returns : path to private - config file . None if the file doesn ' t exist
"""
path = os . path . join ( self . working_dir , ' private-config.cfg ' )
if os . path . exists ( path ) :
return ' private-config.cfg '
else :
return None
2017-06-27 08:09:21 +00:00
@property
def application_id ( self ) :
"""
Returns application_id which unique identifier for IOU running script . Value is between 1 and 512.
When it ' s not set returns value from the local manager.
: returns : integer between 1 and 512
"""
2018-03-12 06:38:50 +00:00
2017-06-27 08:09:21 +00:00
return self . _application_id
@application_id.setter
def application_id ( self , application_id ) :
"""
Sets application_id for IOU .
: param : integer between 1 and 512
"""
self . _application_id = application_id
2015-06-06 21:15:03 +00:00
def extract_configs ( self ) :
"""
Gets the contents of the config files
startup - config and private - config from NVRAM .
: returns : tuple ( startup - config , private - config )
"""
nvram_file = os . path . join ( self . working_dir , " nvram_ {:05d} " . format ( self . application_id ) )
if not os . path . exists ( nvram_file ) :
return None , None
try :
with open ( nvram_file , " rb " ) as file :
nvram_content = file . read ( )
except OSError as e :
log . warning ( " Cannot read nvram file {} : {} " . format ( nvram_file , e ) )
return None , None
try :
startup_config_content , private_config_content = nvram_export ( nvram_content )
except ValueError as e :
2016-02-11 08:15:48 +00:00
log . warning ( " Could not export configs from nvram file {} : {} " . format ( nvram_file , e ) )
2015-06-06 21:15:03 +00:00
return None , None
return startup_config_content , private_config_content
def save_configs ( self ) :
"""
Saves the startup - config and private - config to files .
"""
if self . startup_config_content or self . private_config_content :
startup_config_content , private_config_content = self . extract_configs ( )
if startup_config_content :
config_path = os . path . join ( self . working_dir , " startup-config.cfg " )
try :
config = startup_config_content . decode ( " utf-8 " , errors = " replace " )
with open ( config_path , " wb " ) as f :
log . info ( " saving startup-config to {} " . format ( config_path ) )
f . write ( config . encode ( " utf-8 " ) )
except ( binascii . Error , OSError ) as e :
raise IOUError ( " Could not save the startup configuration {} : {} " . format ( config_path , e ) )
2016-05-22 00:58:28 +00:00
if private_config_content and private_config_content != b ' \n end \n ' :
2015-06-06 21:15:03 +00:00
config_path = os . path . join ( self . working_dir , " private-config.cfg " )
try :
config = private_config_content . decode ( " utf-8 " , errors = " replace " )
with open ( config_path , " wb " ) as f :
log . info ( " saving private-config to {} " . format ( config_path ) )
f . write ( config . encode ( " utf-8 " ) )
except ( binascii . Error , OSError ) as e :
raise IOUError ( " Could not save the private configuration {} : {} " . format ( config_path , e ) )
2018-10-15 10:05:49 +00:00
async def start_capture ( self , adapter_number , port_number , output_file , data_link_type = " DLT_EN10MB " ) :
2015-02-16 19:08:04 +00:00
"""
Starts a packet capture .
2015-04-08 17:17:34 +00:00
: param adapter_number : adapter number
: param port_number : port number
2015-02-16 19:08:04 +00:00
: param output_file : PCAP destination file for the capture
: param data_link_type : PCAP data link type ( DLT_ * ) , default is DLT_EN10MB
"""
2018-10-27 07:47:17 +00:00
nio = self . get_nio ( adapter_number , port_number )
2015-02-16 19:08:04 +00:00
if nio . capturing :
raise IOUError ( " Packet capture is already activated on {adapter_number} / {port_number} " . format ( adapter_number = adapter_number ,
port_number = port_number ) )
2019-04-01 13:58:18 +00:00
nio . start_packet_capture ( output_file , data_link_type )
2015-12-08 15:11:40 +00:00
log . info ( ' IOU " {name} " [ {id} ]: starting packet capture on {adapter_number} / {port_number} to {output_file} ' . format ( name = self . _name ,
2016-01-20 16:10:10 +00:00
id = self . _id ,
adapter_number = adapter_number ,
port_number = port_number ,
output_file = output_file ) )
2015-02-16 19:08:04 +00:00
2016-12-14 11:01:34 +00:00
if self . ubridge :
2016-11-06 10:27:49 +00:00
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( ' iol_bridge start_capture {name} {bay} {unit} " {output_file} " {data_link_type} ' . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
bay = adapter_number ,
unit = port_number ,
output_file = output_file ,
2019-01-17 11:01:58 +00:00
data_link_type = re . sub ( r " ^DLT_ " , " " , data_link_type ) ) )
2015-02-16 19:08:04 +00:00
2018-10-15 10:05:49 +00:00
async def stop_capture ( self , adapter_number , port_number ) :
2015-02-16 19:08:04 +00:00
"""
Stops a packet capture .
2015-04-08 17:17:34 +00:00
: param adapter_number : adapter number
: param port_number : port number
2015-02-16 19:08:04 +00:00
"""
2018-10-27 07:47:17 +00:00
nio = self . get_nio ( adapter_number , port_number )
2019-04-01 12:47:31 +00:00
if not nio . capturing :
return
2019-04-01 13:58:18 +00:00
nio . stop_packet_capture ( )
2015-04-08 17:17:34 +00:00
log . info ( ' IOU " {name} " [ {id} ]: stopping packet capture on {adapter_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
adapter_number = adapter_number ,
port_number = port_number ) )
2016-12-14 11:01:34 +00:00
if self . ubridge :
2016-11-06 10:27:49 +00:00
bridge_name = " IOL-BRIDGE- {} " . format ( self . application_id + 512 )
2018-10-15 10:05:49 +00:00
await self . _ubridge_send ( ' iol_bridge stop_capture {name} {bay} {unit} ' . format ( name = bridge_name ,
2016-11-06 10:27:49 +00:00
bay = adapter_number ,
unit = port_number ) )