2015-02-10 01:24:13 +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/>.
"""
Interface for Dynamips virtual Machine module ( " vm " )
http : / / github . com / GNS3 / dynamips / blob / master / README . hypervisor #L77
"""
import asyncio
import time
import sys
import os
2015-02-16 05:13:24 +00:00
import glob
2015-02-16 23:53:50 +00:00
import base64
2015-03-29 00:09:53 +00:00
import binascii
2015-02-10 01:24:13 +00:00
import logging
log = logging . getLogger ( __name__ )
2016-05-11 17:35:36 +00:00
from . . . base_node import BaseNode
2015-02-16 05:13:24 +00:00
from . . dynamips_error import DynamipsError
2015-02-24 02:00:34 +00:00
from . . nios . nio_udp import NIOUDP
2015-02-26 00:19:37 +00:00
2016-06-13 13:52:31 +00:00
from gns3server . utils . file_watcher import FileWatcher
2015-03-04 15:01:56 +00:00
from gns3server . utils . asyncio import wait_run_in_executor , monitor_process
2015-06-17 15:11:25 +00:00
from gns3server . utils . images import md5sum
2015-02-16 05:13:24 +00:00
2015-02-10 01:24:13 +00:00
2016-05-11 17:35:36 +00:00
class Router ( BaseNode ) :
2015-02-10 01:24:13 +00:00
"""
Dynamips router implementation .
2015-02-11 04:50:02 +00:00
: param name : The name of this router
2016-05-11 17:35:36 +00:00
: param node_id : Node identifier
2015-02-11 04:50:02 +00:00
: param project : Project instance
: param manager : Parent VM Manager
2015-02-12 02:21:34 +00:00
: param dynamips_id : ID to use with Dynamips
2015-02-26 23:15:44 +00:00
: param console : console port
: param aux : auxiliary console port
2015-02-11 04:50:02 +00:00
: param platform : Platform of this router
2015-02-10 01:24:13 +00:00
"""
_status = { 0 : " inactive " ,
1 : " shutting down " ,
2 : " running " ,
3 : " suspended " }
2016-05-11 17:35:36 +00:00
def __init__ ( self , name , node_id , project , manager , dynamips_id = None , console = None , aux = None , platform = " c7200 " , hypervisor = None , ghost_flag = False ) :
2015-02-10 01:24:13 +00:00
2016-02-29 09:38:30 +00:00
allocate_aux = manager . config . get_section_config ( " Dynamips " ) . getboolean ( " allocate_aux_console_ports " , False )
2016-05-11 17:35:36 +00:00
super ( ) . __init__ ( name , node_id , project , manager , console = console , aux = aux , allocate_aux = aux )
2015-02-10 01:24:13 +00:00
2015-02-16 05:13:24 +00:00
self . _hypervisor = hypervisor
2015-02-12 02:21:34 +00:00
self . _dynamips_id = dynamips_id
2015-02-10 01:24:13 +00:00
self . _platform = platform
self . _image = " "
self . _startup_config = " "
self . _private_config = " "
self . _ram = 128 # Megabytes
self . _nvram = 128 # Kilobytes
self . _mmap = True
self . _sparsemem = True
self . _clock_divisor = 8
self . _idlepc = " "
self . _idlemax = 500
self . _idlesleep = 30
self . _ghost_file = " "
self . _ghost_status = 0
if sys . platform . startswith ( " win " ) :
self . _exec_area = 16 # 16 MB by default on Windows (Cygwin)
else :
self . _exec_area = 64 # 64 MB on other systems
self . _disk0 = 0 # Megabytes
self . _disk1 = 0 # Megabytes
2015-06-05 20:54:22 +00:00
self . _auto_delete_disks = False
2015-02-12 02:21:34 +00:00
self . _mac_addr = " "
2015-02-10 01:24:13 +00:00
self . _system_id = " FTX0945W0MY " # processor board ID in IOS
self . _slots = [ ]
self . _ghost_flag = ghost_flag
2016-06-13 13:52:31 +00:00
self . _memory_watcher = None
2015-02-10 01:24:13 +00:00
if not ghost_flag :
2015-02-12 02:21:34 +00:00
if not dynamips_id :
2015-10-07 09:34:27 +00:00
self . _dynamips_id = manager . get_dynamips_id ( project . id )
2015-02-12 02:21:34 +00:00
else :
2015-10-07 09:34:27 +00:00
self . _dynamips_id = dynamips_id
manager . take_dynamips_id ( project . id , dynamips_id )
2015-02-12 02:21:34 +00:00
else :
2015-02-16 05:13:24 +00:00
log . info ( " Creating a new ghost IOS instance " )
2015-02-24 00:42:55 +00:00
if self . _console :
# Ghost VMs do not need a console port.
2016-02-29 09:38:30 +00:00
self . console = None
2015-02-12 02:21:34 +00:00
self . _dynamips_id = 0
self . _name = " Ghost "
2015-02-10 01:24:13 +00:00
def __json__ ( self ) :
router_info = { " name " : self . name ,
2016-05-11 17:35:36 +00:00
" node_id " : self . id ,
2016-05-12 08:39:50 +00:00
" node_directory " : os . path . join ( self . project . module_working_directory ( self . manager . module_name . lower ( ) ) ) ,
2015-02-10 01:24:13 +00:00
" project_id " : self . project . id ,
2015-02-12 02:21:34 +00:00
" dynamips_id " : self . _dynamips_id ,
2015-02-10 01:24:13 +00:00
" platform " : self . _platform ,
" image " : self . _image ,
2015-06-17 15:11:25 +00:00
" image_md5sum " : md5sum ( self . _image ) ,
2015-02-10 01:24:13 +00:00
" startup_config " : self . _startup_config ,
" private_config " : self . _private_config ,
" ram " : self . _ram ,
" nvram " : self . _nvram ,
" mmap " : self . _mmap ,
" sparsemem " : self . _sparsemem ,
" clock_divisor " : self . _clock_divisor ,
" idlepc " : self . _idlepc ,
" idlemax " : self . _idlemax ,
" idlesleep " : self . _idlesleep ,
" exec_area " : self . _exec_area ,
" disk0 " : self . _disk0 ,
" disk1 " : self . _disk1 ,
2015-06-05 20:54:22 +00:00
" auto_delete_disks " : self . _auto_delete_disks ,
2016-05-17 17:51:06 +00:00
" status " : self . status ,
2016-02-29 09:38:30 +00:00
" console " : self . console ,
2016-03-15 09:45:05 +00:00
" console_type " : " telnet " ,
2016-02-29 09:38:30 +00:00
" aux " : self . aux ,
2015-02-10 01:24:13 +00:00
" mac_addr " : self . _mac_addr ,
" system_id " : self . _system_id }
2015-03-11 21:04:11 +00:00
# return the relative path if the IOS image is in the images_path directory
2015-04-14 16:46:55 +00:00
router_info [ " image " ] = self . manager . get_relative_image_path ( self . _image )
2015-03-11 21:04:11 +00:00
2015-02-16 23:53:50 +00:00
# add the slots
slot_number = 0
for slot in self . _slots :
if slot :
slot = str ( slot )
2015-07-26 21:51:55 +00:00
router_info [ " slot " + str ( slot_number ) ] = slot
2015-02-16 23:53:50 +00:00
slot_number + = 1
# add the wics
2015-03-04 15:01:56 +00:00
if len ( self . _slots ) > 0 and self . _slots [ 0 ] and self . _slots [ 0 ] . wics :
2015-02-16 23:53:50 +00:00
for wic_slot_number in range ( 0 , len ( self . _slots [ 0 ] . wics ) ) :
if self . _slots [ 0 ] . wics [ wic_slot_number ] :
router_info [ " wic " + str ( wic_slot_number ) ] = str ( self . _slots [ 0 ] . wics [ wic_slot_number ] )
2015-07-26 21:51:55 +00:00
else :
router_info [ " wic " + str ( wic_slot_number ) ] = None
2015-02-10 01:24:13 +00:00
return router_info
2016-06-13 13:52:31 +00:00
def _memory_changed ( self , path ) :
"""
Called when the NVRAM file has changed
"""
asyncio . async ( self . save_configs ( ) )
2015-02-16 05:13:24 +00:00
@property
def dynamips_id ( self ) :
"""
Returns the Dynamips VM ID .
: return : Dynamips VM identifier
"""
return self . _dynamips_id
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def create ( self ) :
2015-02-16 05:13:24 +00:00
if not self . _hypervisor :
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
self . _hypervisor = yield from self . manager . start_new_hypervisor ( working_dir = module_workdir )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm create " {name} " {id} {platform} ' . format ( name = self . _name ,
2015-02-12 02:21:34 +00:00
id = self . _dynamips_id ,
2015-02-10 01:24:13 +00:00
platform = self . _platform ) )
if not self . _ghost_flag :
2015-02-11 04:50:02 +00:00
log . info ( ' Router {platform} " {name} " [ {id} ] has been created ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
platform = self . _platform ,
id = self . _id ) )
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_con_tcp_port " {name} " {console} ' . format ( name = self . _name , console = self . _console ) )
2015-02-26 23:15:44 +00:00
2016-02-29 09:38:30 +00:00
if self . aux is not None :
yield from self . _hypervisor . send ( ' vm set_aux_tcp_port " {name} " {aux} ' . format ( name = self . _name , aux = self . aux ) )
2015-02-10 01:24:13 +00:00
# get the default base MAC address
2015-02-11 04:50:02 +00:00
mac_addr = yield from self . _hypervisor . send ( ' {platform} get_mac_addr " {name} " ' . format ( platform = self . _platform ,
2015-02-10 01:24:13 +00:00
name = self . _name ) )
self . _mac_addr = mac_addr [ 0 ]
self . _hypervisor . devices . append ( self )
@asyncio.coroutine
def get_status ( self ) :
"""
Returns the status of this router
: returns : inactive , shutting down , running or suspended .
"""
2015-02-11 04:50:02 +00:00
status = yield from self . _hypervisor . send ( ' vm get_status " {name} " ' . format ( name = self . _name ) )
2015-04-01 15:39:37 +00:00
if len ( status ) == 0 :
raise DynamipsError ( " Can ' t get vm {name} status " . format ( name = self . _name ) )
2015-02-10 01:24:13 +00:00
return self . _status [ int ( status [ 0 ] ) ]
@asyncio.coroutine
def start ( self ) :
"""
Starts this router .
At least the IOS image must be set before it can start .
"""
status = yield from self . get_status ( )
if status == " suspended " :
yield from self . resume ( )
elif status == " inactive " :
if not os . path . isfile ( self . _image ) or not os . path . exists ( self . _image ) :
if os . path . islink ( self . _image ) :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' IOS image " {} " linked to " {} " is not accessible ' . format ( self . _image , os . path . realpath ( self . _image ) ) )
2015-02-10 01:24:13 +00:00
else :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' IOS image " {} " is not accessible ' . format ( self . _image ) )
2015-02-10 01:24:13 +00:00
try :
with open ( self . _image , " rb " ) as f :
# read the first 7 bytes of the file.
elf_header_start = f . read ( 7 )
except OSError as e :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' Cannot read ELF header for IOS image " {} " : {} ' . format ( self . _image , e ) )
2015-02-10 01:24:13 +00:00
# IOS images must start with the ELF magic number, be 32-bit, big endian and have an ELF version of 1
if elf_header_start != b ' \x7f ELF \x01 \x02 \x01 ' :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' " {} " is not a valid IOS image ' . format ( self . _image ) )
2015-02-10 01:24:13 +00:00
2015-10-12 21:57:37 +00:00
# check if there is enough RAM to run
if not self . _ghost_flag :
self . check_available_ram ( self . ram )
2015-02-13 02:15:35 +00:00
yield from self . _hypervisor . send ( ' vm start " {name} " ' . format ( name = self . _name ) )
2015-03-04 15:01:56 +00:00
self . status = " started "
2015-02-11 04:50:02 +00:00
log . info ( ' router " {name} " [ {id} ] has been started ' . format ( name = self . _name , id = self . _id ) )
2016-06-13 13:52:31 +00:00
self . _memory_watcher = FileWatcher ( self . _memory_files ( ) , self . _memory_changed , strategy = ' hash ' , delay = 30 )
2015-03-04 15:01:56 +00:00
monitor_process ( self . _hypervisor . process , self . _termination_callback )
@asyncio.coroutine
def _termination_callback ( self , returncode ) :
"""
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-07-04 20:08:03 +00:00
if self . status == " started " :
self . status = " stopped "
log . info ( " Dynamips hypervisor process has stopped, return code: %d " , returncode )
if returncode != 0 :
self . project . emit ( " log.error " , { " message " : " Dynamips hypervisor process has stopped, return code: {} \n {} " . format ( returncode , self . _hypervisor . read_stdout ( ) ) } )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def stop ( self ) :
"""
Stops this router .
"""
status = yield from self . get_status ( )
if status != " inactive " :
2016-05-22 01:13:36 +00:00
try :
yield from self . _hypervisor . send ( ' vm stop " {name} " ' . format ( name = self . _name ) )
except DynamipsError as e :
log . warn ( " Could not stop {} : {} " . format ( self . _name , e ) )
2015-03-04 15:01:56 +00:00
self . status = " stopped "
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ] has been stopped ' . format ( name = self . _name , id = self . _id ) )
2016-06-13 13:52:31 +00:00
if self . _memory_watcher :
self . _memory_watcher . close ( )
self . _memory_watcher = None
2016-01-15 17:02:52 +00:00
yield from self . save_configs ( )
2015-02-10 01:24:13 +00:00
2015-02-13 02:15:35 +00:00
@asyncio.coroutine
def reload ( self ) :
"""
Reload this router .
"""
yield from self . stop ( )
yield from self . start ( )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def suspend ( self ) :
"""
Suspends this router .
"""
status = yield from self . get_status ( )
if status == " running " :
2015-02-13 02:15:35 +00:00
yield from self . _hypervisor . send ( ' vm suspend " {name} " ' . format ( name = self . _name ) )
2016-05-14 02:41:58 +00:00
self . status = " suspended "
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ] has been suspended ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def resume ( self ) :
"""
Resumes this suspended router
"""
2015-02-13 02:15:35 +00:00
yield from self . _hypervisor . send ( ' vm resume " {name} " ' . format ( name = self . _name ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ] has been resumed ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def is_running ( self ) :
"""
Checks if this router is running .
: returns : True if running , False otherwise
"""
status = yield from self . get_status ( )
if status == " running " :
return True
return False
@asyncio.coroutine
def close ( self ) :
2016-02-29 09:38:30 +00:00
if not ( yield from super ( ) . close ( ) ) :
return False
2015-02-24 02:00:34 +00:00
for adapter in self . _slots :
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
2015-02-22 00:24:39 +00:00
if self in self . _hypervisor . devices :
self . _hypervisor . devices . remove ( self )
2015-02-15 19:18:12 +00:00
if self . _hypervisor and not self . _hypervisor . devices :
try :
yield from self . stop ( )
2015-02-16 23:53:50 +00:00
yield from self . _hypervisor . send ( ' vm delete " {} " ' . format ( self . _name ) )
2016-05-22 01:13:36 +00:00
except DynamipsError as e :
log . warn ( " Could not stop and delete {} : {} " . format ( self . _name , e ) )
2015-02-10 01:24:13 +00:00
yield from self . hypervisor . stop ( )
2015-06-05 20:54:22 +00:00
if self . _auto_delete_disks :
# delete nvram and disk files
project_dir = os . path . join ( self . project . module_working_directory ( self . manager . module_name . lower ( ) ) )
2015-10-07 14:44:50 +00:00
files = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _disk[0-1] " . format ( self . platform , self . dynamips_id ) ) )
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _slot[0-1] " . format ( self . platform , self . dynamips_id ) ) )
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _nvram " . format ( self . platform , self . dynamips_id ) ) )
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _flash[0-1] " . format ( self . platform , self . dynamips_id ) ) )
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _rom " . format ( self . platform , self . dynamips_id ) ) )
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _bootflash " . format ( self . platform , self . dynamips_id ) ) )
2016-02-02 09:05:08 +00:00
files + = glob . glob ( os . path . join ( glob . escape ( project_dir ) , " {} _i {} _ssa " . format ( self . platform , self . dynamips_id ) ) )
2015-06-05 20:54:22 +00:00
for file in files :
try :
log . debug ( " Deleting file {} " . format ( file ) )
yield from wait_run_in_executor ( os . remove , file )
except OSError as e :
log . warn ( " Could not delete file {} : {} " . format ( file , e ) )
continue
2015-02-10 01:24:13 +00:00
@property
def platform ( self ) :
"""
Returns the platform of this router .
: returns : platform name ( string ) :
c7200 , c3745 , c3725 , c3600 , c2691 , c2600 or c1700
"""
return self . _platform
@property
def hypervisor ( self ) :
"""
Returns the current hypervisor .
: returns : hypervisor instance
"""
return self . _hypervisor
@asyncio.coroutine
def list ( self ) :
"""
Returns all VM instances
: returns : list of all VM instances
"""
vm_list = yield from self . _hypervisor . send ( " vm list " )
return vm_list
@asyncio.coroutine
def list_con_ports ( self ) :
"""
Returns all VM console TCP ports
: returns : list of port numbers
"""
port_list = yield from self . _hypervisor . send ( " vm list_con_ports " )
return port_list
@asyncio.coroutine
def set_debug_level ( self , level ) :
"""
Sets the debug level for this router ( default is 0 ) .
: param level : level number
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_debug_level " {name} " {level} ' . format ( name = self . _name , level = level ) )
2015-02-10 01:24:13 +00:00
@property
def image ( self ) :
"""
Returns this IOS image for this router .
: returns : path to IOS image file
"""
return self . _image
@asyncio.coroutine
def set_image ( self , image ) :
"""
Sets the IOS image for this router .
There is no default .
: param image : path to IOS image file
"""
2015-04-14 16:46:55 +00:00
image = self . manager . get_abs_image_path ( image )
2015-02-26 00:19:37 +00:00
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_ios " {name} " " {image} " ' . format ( name = self . _name , image = image ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: has a new IOS image set: " {image} " ' . format ( name = self . _name ,
id = self . _id ,
image = image ) )
2015-02-10 01:24:13 +00:00
self . _image = image
@property
def ram ( self ) :
"""
Returns the amount of RAM allocated to this router .
: returns : amount of RAM in Mbytes ( integer )
"""
return self . _ram
@asyncio.coroutine
def set_ram ( self , ram ) :
"""
Sets amount of RAM allocated to this router
: param ram : amount of RAM in Mbytes ( integer )
"""
if self . _ram == ram :
return
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_ram " {name} " {ram} ' . format ( name = self . _name , ram = ram ) )
log . info ( ' Router " {name} " [ {id} ]: RAM updated from {old_ram} MB to {new_ram} MB ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_ram = self . _ram ,
new_ram = ram ) )
self . _ram = ram
@property
def nvram ( self ) :
"""
Returns the mount of NVRAM allocated to this router .
: returns : amount of NVRAM in Kbytes ( integer )
"""
return self . _nvram
@asyncio.coroutine
def set_nvram ( self , nvram ) :
"""
Sets amount of NVRAM allocated to this router
: param nvram : amount of NVRAM in Kbytes ( integer )
"""
if self . _nvram == nvram :
return
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_nvram " {name} " {nvram} ' . format ( name = self . _name , nvram = nvram ) )
log . info ( ' Router " {name} " [ {id} ]: NVRAM updated from {old_nvram} KB to {new_nvram} KB ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_nvram = self . _nvram ,
new_nvram = nvram ) )
self . _nvram = nvram
@property
def mmap ( self ) :
"""
Returns True if a mapped file is used to simulate this router memory .
: returns : boolean either mmap is activated or not
"""
return self . _mmap
@asyncio.coroutine
def set_mmap ( self , mmap ) :
"""
Enable / Disable use of a mapped file to simulate router memory .
By default , a mapped file is used . This is a bit slower , but requires less memory .
: param mmap : activate / deactivate mmap ( boolean )
"""
if mmap :
flag = 1
else :
flag = 0
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_ram_mmap " {name} " {mmap} ' . format ( name = self . _name , mmap = flag ) )
2015-02-10 01:24:13 +00:00
if mmap :
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: mmap enabled ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
else :
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: mmap disabled ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
self . _mmap = mmap
@property
def sparsemem ( self ) :
"""
Returns True if sparse memory is used on this router .
: returns : boolean either mmap is activated or not
"""
return self . _sparsemem
@asyncio.coroutine
def set_sparsemem ( self , sparsemem ) :
"""
Enable / disable use of sparse memory
: param sparsemem : activate / deactivate sparsemem ( boolean )
"""
if sparsemem :
flag = 1
else :
flag = 0
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_sparse_mem " {name} " {sparsemem} ' . format ( name = self . _name , sparsemem = flag ) )
2015-02-10 01:24:13 +00:00
if sparsemem :
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: sparse memory enabled ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
else :
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: sparse memory disabled ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
self . _sparsemem = sparsemem
@property
def clock_divisor ( self ) :
"""
Returns the clock divisor value for this router .
: returns : clock divisor value ( integer )
"""
return self . _clock_divisor
@asyncio.coroutine
def set_clock_divisor ( self , clock_divisor ) :
"""
Sets the clock divisor value . The higher is the value , the faster is the clock in the
virtual machine . The default is 4 , but it is often required to adjust it .
: param clock_divisor : clock divisor value ( integer )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_clock_divisor " {name} " {clock} ' . format ( name = self . _name , clock = clock_divisor ) )
log . info ( ' Router " {name} " [ {id} ]: clock divisor updated from {old_clock} to {new_clock} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_clock = self . _clock_divisor ,
new_clock = clock_divisor ) )
self . _clock_divisor = clock_divisor
@property
def idlepc ( self ) :
"""
Returns the idle Pointer Counter ( PC ) .
: returns : idlepc value ( string )
"""
return self . _idlepc
@asyncio.coroutine
def set_idlepc ( self , idlepc ) :
"""
Sets the idle Pointer Counter ( PC )
: param idlepc : idlepc value ( string )
"""
if not idlepc :
idlepc = " 0x0 "
is_running = yield from self . is_running ( )
if not is_running :
# router is not running
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_idle_pc " {name} " {idlepc} ' . format ( name = self . _name , idlepc = idlepc ) )
2015-02-10 01:24:13 +00:00
else :
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_idle_pc_online " {name} " 0 {idlepc} ' . format ( name = self . _name , idlepc = idlepc ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: idle-PC set to {idlepc} ' . format ( name = self . _name , id = self . _id , idlepc = idlepc ) )
2015-02-10 01:24:13 +00:00
self . _idlepc = idlepc
@asyncio.coroutine
def get_idle_pc_prop ( self ) :
"""
Gets the idle PC proposals .
Takes 1000 measurements and records up to 10 idle PC proposals .
There is a 10 ms wait between each measurement .
: returns : list of idle PC proposal
"""
is_running = yield from self . is_running ( )
2016-05-19 14:21:35 +00:00
was_auto_started = False
2015-02-10 01:24:13 +00:00
if not is_running :
2016-05-19 14:21:35 +00:00
yield from self . start ( )
was_auto_started = True
yield from asyncio . sleep ( 20 ) # leave time to the router to boot
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ] has started calculating Idle-PC values ' . format ( name = self . _name , id = self . _id ) )
2015-02-10 01:24:13 +00:00
begin = time . time ( )
2015-02-11 04:50:02 +00:00
idlepcs = yield from self . _hypervisor . send ( ' vm get_idle_pc_prop " {} " 0 ' . format ( self . _name ) )
log . info ( ' Router " {name} " [ {id} ] has finished calculating Idle-PC values after {time:.4f} seconds ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
time = time . time ( ) - begin ) )
2016-05-19 14:21:35 +00:00
if was_auto_started :
yield from self . stop ( )
2015-02-10 01:24:13 +00:00
return idlepcs
@asyncio.coroutine
def show_idle_pc_prop ( self ) :
"""
Dumps the idle PC proposals ( previously generated ) .
: returns : list of idle PC proposal
"""
is_running = yield from self . is_running ( )
if not is_running :
# router is not running
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' Router " {name} " is not running ' . format ( name = self . _name ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
proposals = yield from self . _hypervisor . send ( ' vm show_idle_pc_prop " {} " 0 ' . format ( self . _name ) )
2015-02-10 01:24:13 +00:00
return proposals
@property
def idlemax ( self ) :
"""
Returns CPU idle max value .
: returns : idle max ( integer )
"""
return self . _idlemax
@asyncio.coroutine
def set_idlemax ( self , idlemax ) :
"""
Sets CPU idle max value
: param idlemax : idle max value ( integer )
"""
is_running = yield from self . is_running ( )
if is_running : # router is running
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_idle_max " {name} " 0 {idlemax} ' . format ( name = self . _name , idlemax = idlemax ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: idlemax updated from {old_idlemax} to {new_idlemax} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_idlemax = self . _idlemax ,
new_idlemax = idlemax ) )
self . _idlemax = idlemax
@property
def idlesleep ( self ) :
"""
Returns CPU idle sleep time value .
: returns : idle sleep ( integer )
"""
return self . _idlesleep
@asyncio.coroutine
def set_idlesleep ( self , idlesleep ) :
"""
Sets CPU idle sleep time value .
: param idlesleep : idle sleep value ( integer )
"""
is_running = yield from self . is_running ( )
if is_running : # router is running
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_idle_sleep_time " {name} " 0 {idlesleep} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
idlesleep = idlesleep ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: idlesleep updated from {old_idlesleep} to {new_idlesleep} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_idlesleep = self . _idlesleep ,
new_idlesleep = idlesleep ) )
self . _idlesleep = idlesleep
@property
def ghost_file ( self ) :
"""
Returns ghost RAM file .
: returns : path to ghost file
"""
return self . _ghost_file
@asyncio.coroutine
def set_ghost_file ( self , ghost_file ) :
"""
Sets ghost RAM file
: ghost_file : path to ghost file
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_ghost_file " {name} " {ghost_file} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
ghost_file = ghost_file ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: ghost file set to {ghost_file} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
ghost_file = ghost_file ) )
self . _ghost_file = ghost_file
def formatted_ghost_file ( self ) :
"""
Returns a properly formatted ghost file name .
: returns : formatted ghost_file name ( string )
"""
# replace specials characters in 'drive:\filename' in Linux and Dynamips in MS Windows or viceversa.
ghost_file = " {} - {} .ghost " . format ( os . path . basename ( self . _image ) , self . _ram )
ghost_file = ghost_file . replace ( ' \\ ' , ' - ' ) . replace ( ' / ' , ' - ' ) . replace ( ' : ' , ' - ' )
return ghost_file
@property
def ghost_status ( self ) :
""" Returns ghost RAM status
: returns : ghost status ( integer )
"""
return self . _ghost_status
@asyncio.coroutine
def set_ghost_status ( self , ghost_status ) :
"""
Sets ghost RAM status
: param ghost_status : state flag indicating status
0 = > Do not use IOS ghosting
1 = > This is a ghost instance
2 = > Use an existing ghost instance
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_ghost_status " {name} " {ghost_status} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
ghost_status = ghost_status ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: ghost status set to {ghost_status} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
ghost_status = ghost_status ) )
self . _ghost_status = ghost_status
@property
def exec_area ( self ) :
"""
Returns the exec area value .
: returns : exec area value ( integer )
"""
return self . _exec_area
@asyncio.coroutine
def set_exec_area ( self , exec_area ) :
"""
Sets the exec area value .
The exec area is a pool of host memory used to store pages
translated by the JIT ( they contain the native code
corresponding to MIPS code pages ) .
: param exec_area : exec area value ( integer )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_exec_area " {name} " {exec_area} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
exec_area = exec_area ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: exec area updated from {old_exec} MB to {new_exec} MB ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_exec = self . _exec_area ,
new_exec = exec_area ) )
self . _exec_area = exec_area
@property
def disk0 ( self ) :
"""
Returns the size ( MB ) for PCMCIA disk0 .
: returns : disk0 size ( integer )
"""
return self . _disk0
@asyncio.coroutine
def set_disk0 ( self , disk0 ) :
"""
Sets the size ( MB ) for PCMCIA disk0 .
: param disk0 : disk0 size ( integer )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_disk0 " {name} " {disk0} ' . format ( name = self . _name , disk0 = disk0 ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: disk0 updated from {old_disk0} MB to {new_disk0} MB ' . format ( name = self . _name ,
id = self . _id ,
old_disk0 = self . _disk0 ,
new_disk0 = disk0 ) )
2015-02-10 01:24:13 +00:00
self . _disk0 = disk0
@property
def disk1 ( self ) :
"""
Returns the size ( MB ) for PCMCIA disk1 .
: returns : disk1 size ( integer )
"""
return self . _disk1
@asyncio.coroutine
2015-05-27 16:17:46 +00:00
def set_disk1 ( self , disk1 ) :
2015-02-10 01:24:13 +00:00
"""
Sets the size ( MB ) for PCMCIA disk1 .
: param disk1 : disk1 size ( integer )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_disk1 " {name} " {disk1} ' . format ( name = self . _name , disk1 = disk1 ) )
2015-02-10 01:24:13 +00:00
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: disk1 updated from {old_disk1} MB to {new_disk1} MB ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_disk1 = self . _disk1 ,
new_disk1 = disk1 ) )
self . _disk1 = disk1
2015-06-05 20:54:22 +00:00
@property
def auto_delete_disks ( self ) :
"""
Returns True if auto delete disks is enabled on this router .
: returns : boolean either auto delete disks is activated or not
"""
return self . _auto_delete_disks
@asyncio.coroutine
def set_auto_delete_disks ( self , auto_delete_disks ) :
"""
Enable / disable use of auto delete disks
: param auto_delete_disks : activate / deactivate auto delete disks ( boolean )
"""
if auto_delete_disks :
log . info ( ' Router " {name} " [ {id} ]: auto delete disks enabled ' . format ( name = self . _name , id = self . _id ) )
else :
log . info ( ' Router " {name} " [ {id} ]: auto delete disks disabled ' . format ( name = self . _name , id = self . _id ) )
self . _auto_delete_disks = auto_delete_disks
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def set_console ( self , console ) :
"""
Sets the TCP console port .
: param console : console port ( integer )
"""
2016-02-29 09:38:30 +00:00
self . console = console
2016-03-31 08:06:38 +00:00
yield from self . _hypervisor . send ( ' vm set_con_tcp_port " {name} " {console} ' . format ( name = self . _name , console = self . console ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def set_aux ( self , aux ) :
"""
Sets the TCP auxiliary port .
: param aux : console auxiliary port ( integer )
"""
2016-02-29 09:38:30 +00:00
self . aux = aux
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' vm set_aux_tcp_port " {name} " {aux} ' . format ( name = self . _name , aux = aux ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
def get_cpu_usage ( self , cpu_id = 0 ) :
"""
Shows cpu usage in seconds , " cpu_id " is ignored .
: returns : cpu usage in seconds
"""
2015-02-11 04:50:02 +00:00
cpu_usage = yield from self . _hypervisor . send ( ' vm cpu_usage " {name} " {cpu_id} ' . format ( name = self . _name , cpu_id = cpu_id ) )
2015-02-10 01:24:13 +00:00
return int ( cpu_usage [ 0 ] )
@property
def mac_addr ( self ) :
"""
Returns the MAC address .
: returns : the MAC address ( hexadecimal format : hh : hh : hh : hh : hh : hh )
"""
return self . _mac_addr
@asyncio.coroutine
def set_mac_addr ( self , mac_addr ) :
"""
Sets the MAC address .
: param mac_addr : a MAC address ( hexadecimal format : hh : hh : hh : hh : hh : hh )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' {platform} set_mac_addr " {name} " {mac_addr} ' . format ( platform = self . _platform ,
2015-02-10 01:24:13 +00:00
name = self . _name ,
mac_addr = mac_addr ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: MAC address updated from {old_mac} to {new_mac} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_mac = self . _mac_addr ,
new_mac = mac_addr ) )
self . _mac_addr = mac_addr
@property
def system_id ( self ) :
"""
Returns the system ID .
: returns : the system ID ( also called board processor ID )
"""
return self . _system_id
@asyncio.coroutine
def set_system_id ( self , system_id ) :
"""
Sets the system ID .
: param system_id : a system ID ( also called board processor ID )
"""
2015-02-11 04:50:02 +00:00
yield from self . _hypervisor . send ( ' {platform} set_system_id " {name} " {system_id} ' . format ( platform = self . _platform ,
2015-02-10 01:24:13 +00:00
name = self . _name ,
system_id = system_id ) )
2015-02-11 04:50:02 +00:00
log . info ( ' Router " {name} " [ {id} ]: system ID updated from {old_id} to {new_id} ' . format ( name = self . _name ,
2015-02-10 01:24:13 +00:00
id = self . _id ,
old_id = self . _system_id ,
new_id = system_id ) )
self . _system_id = system_id
@asyncio.coroutine
def get_slot_bindings ( self ) :
"""
Returns slot bindings .
: returns : slot bindings ( adapter names ) list
"""
2015-02-11 04:50:02 +00:00
slot_bindings = yield from self . _hypervisor . send ( ' vm slot_bindings " {} " ' . format ( self . _name ) )
2015-02-10 01:24:13 +00:00
return slot_bindings
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_add_binding ( self , slot_number , adapter ) :
2015-02-10 01:24:13 +00:00
"""
Adds a slot binding ( a module into a slot ) .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
2015-02-10 01:24:13 +00:00
: param adapter : device to add in the corresponding slot
"""
try :
2015-02-13 22:11:14 +00:00
slot = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name , slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
if slot is not None :
current_adapter = slot
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} is already occupied by adapter {adapter} on router " {name} " ' . format ( name = self . _name ,
2015-02-19 10:33:25 +00:00
slot_number = slot_number ,
adapter = current_adapter ) )
2015-02-10 01:24:13 +00:00
is_running = yield from self . is_running ( )
# Only c7200, c3600 and c3745 (NM-4T only) support new adapter while running
if is_running and not ( ( self . _platform == ' c7200 ' and not str ( adapter ) . startswith ( ' C7200 ' ) )
2015-02-13 13:43:28 +00:00
and not ( self . _platform == ' c3600 ' and self . chassis == ' 3660 ' )
and not ( self . _platform == ' c3745 ' and adapter == ' NM-4T ' ) ) :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' Adapter {adapter} cannot be added while router " {name} " is running ' . format ( adapter = adapter ,
2015-02-10 01:24:13 +00:00
name = self . _name ) )
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_add_binding " {name} " {slot_number} 0 {adapter} ' . format ( name = self . _name ,
slot_number = slot_number ,
adapter = adapter ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: adapter {adapter} inserted into slot {slot_number} ' . format ( name = self . _name ,
id = self . _id ,
adapter = adapter ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
self . _slots [ slot_number ] = adapter
2015-02-10 01:24:13 +00:00
# Generate an OIR event if the router is running
if is_running :
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_oir_start " {name} " {slot_number} 0 ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: OIR start event sent to slot {slot_number} ' . format ( name = self . _name ,
id = self . _id ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_remove_binding ( self , slot_number ) :
2015-02-10 01:24:13 +00:00
"""
Removes a slot binding ( a module from a slot ) .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
2015-02-10 01:24:13 +00:00
"""
try :
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
if adapter is None :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' No adapter in slot {slot_number} on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
is_running = yield from self . is_running ( )
# Only c7200, c3600 and c3745 (NM-4T only) support to remove adapter while running
if is_running and not ( ( self . _platform == ' c7200 ' and not str ( adapter ) . startswith ( ' C7200 ' ) )
2015-02-13 13:43:28 +00:00
and not ( self . _platform == ' c3600 ' and self . chassis == ' 3660 ' )
and not ( self . _platform == ' c3745 ' and adapter == ' NM-4T ' ) ) :
2015-02-11 04:50:02 +00:00
raise DynamipsError ( ' Adapter {adapter} cannot be removed while router " {name} " is running ' . format ( adapter = adapter ,
2015-02-10 01:24:13 +00:00
name = self . _name ) )
# Generate an OIR event if the router is running
if is_running :
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_oir_stop " {name} " {slot_number} 0 ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: OIR stop event sent to slot {slot_number} ' . format ( name = self . _name ,
2015-02-19 10:33:25 +00:00
id = self . _id ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_remove_binding " {name} " {slot_number} 0 ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: adapter {adapter} removed from slot {slot_number} ' . format ( name = self . _name ,
2015-02-19 10:33:25 +00:00
id = self . _id ,
adapter = adapter ,
slot_number = slot_number ) )
2015-02-13 22:11:14 +00:00
self . _slots [ slot_number ] = None
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def install_wic ( self , wic_slot_number , wic ) :
2015-02-10 01:24:13 +00:00
"""
Installs a WIC adapter into this router .
2015-02-13 22:11:14 +00:00
: param wic_slot_number : WIC slot number
2015-02-10 01:24:13 +00:00
: param wic : WIC to be installed
"""
# WICs are always installed on adapters in slot 0
2015-02-13 22:11:14 +00:00
slot_number = 0
2015-02-10 01:24:13 +00:00
# Do not check if slot has an adapter because adapters with WICs interfaces
# must be inserted by default in the router and cannot be removed.
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
if wic_slot_number > len ( adapter . wics ) - 1 :
raise DynamipsError ( " WIC slot {wic_slot_number} doesn ' t exist " . format ( wic_slot_number = wic_slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
if not adapter . wic_slot_available ( wic_slot_number ) :
raise DynamipsError ( " WIC slot {wic_slot_number} is already occupied by another WIC " . format ( wic_slot_number = wic_slot_number ) )
2015-02-10 01:24:13 +00:00
# Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48
2015-02-13 22:11:14 +00:00
internal_wic_slot_number = 16 * ( wic_slot_number + 1 )
yield from self . _hypervisor . send ( ' vm slot_add_binding " {name} " {slot_number} {wic_slot_number} {wic} ' . format ( name = self . _name ,
2015-02-19 10:33:25 +00:00
slot_number = slot_number ,
wic_slot_number = internal_wic_slot_number ,
wic = wic ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: {wic} inserted into WIC slot {wic_slot_number} ' . format ( name = self . _name ,
2015-02-19 10:33:25 +00:00
id = self . _id ,
wic = wic ,
wic_slot_number = wic_slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
adapter . install_wic ( wic_slot_number , wic )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def uninstall_wic ( self , wic_slot_number ) :
2015-02-10 01:24:13 +00:00
"""
Uninstalls a WIC adapter from this router .
2015-02-13 22:11:14 +00:00
: param wic_slot_number : WIC slot number
2015-02-10 01:24:13 +00:00
"""
# WICs are always installed on adapters in slot 0
2015-02-13 22:11:14 +00:00
slot_number = 0
2015-02-10 01:24:13 +00:00
# Do not check if slot has an adapter because adapters with WICs interfaces
# must be inserted by default in the router and cannot be removed.
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
if wic_slot_number > len ( adapter . wics ) - 1 :
raise DynamipsError ( " WIC slot {wic_slot_number} doesn ' t exist " . format ( wic_slot_number = wic_slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
if adapter . wic_slot_available ( wic_slot_number ) :
raise DynamipsError ( " No WIC is installed in WIC slot {wic_slot_number} " . format ( wic_slot_number = wic_slot_number ) )
2015-02-10 01:24:13 +00:00
# Dynamips WICs slot IDs start on a multiple of 16
# WIC1 = 16, WIC2 = 32 and WIC3 = 48
2015-02-13 22:11:14 +00:00
internal_wic_slot_number = 16 * ( wic_slot_number + 1 )
yield from self . _hypervisor . send ( ' vm slot_remove_binding " {name} " {slot_number} {wic_slot_number} ' . format ( name = self . _name ,
slot_number = slot_number ,
wic_slot_number = internal_wic_slot_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: {wic} removed from WIC slot {wic_slot_number} ' . format ( name = self . _name ,
id = self . _id ,
wic = adapter . wics [ wic_slot_number ] ,
wic_slot_number = wic_slot_number ) )
adapter . uninstall_wic ( wic_slot_number )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def get_slot_nio_bindings ( self , slot_number ) :
2015-02-10 01:24:13 +00:00
"""
Returns slot NIO bindings .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
2015-02-10 01:24:13 +00:00
: returns : list of NIO bindings
"""
2015-02-13 22:11:14 +00:00
nio_bindings = yield from self . _hypervisor . send ( ' vm slot_nio_bindings " {name} " {slot_number} ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-02-10 01:24:13 +00:00
return nio_bindings
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_add_nio_binding ( self , slot_number , port_number , nio ) :
2015-02-10 01:24:13 +00:00
"""
Adds a slot NIO binding .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
: param nio : NIO instance to add to the slot / port
"""
try :
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-03-27 15:20:31 +00:00
if adapter is None :
2015-03-29 00:09:53 +00:00
raise DynamipsError ( " Adapter is missing in slot {slot_number} " . format ( slot_number = slot_number ) )
2015-03-27 15:20:31 +00:00
2015-02-13 22:11:14 +00:00
if not adapter . port_exists ( port_number ) :
raise DynamipsError ( " Port {port_number} does not exist in adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
2015-07-21 01:22:20 +00:00
try :
yield from self . _hypervisor . send ( ' vm slot_add_nio_binding " {name} " {slot_number} {port_number} {nio} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ,
nio = nio ) )
except DynamipsError :
# in case of error try to remove and add the nio binding
yield from self . _hypervisor . send ( ' vm slot_remove_nio_binding " {name} " {slot_number} {port_number} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ) )
yield from self . _hypervisor . send ( ' vm slot_add_nio_binding " {name} " {slot_number} {port_number} {nio} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ,
nio = nio ) )
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: NIO {nio_name} bound to port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio_name = nio . name ,
slot_number = slot_number ,
port_number = port_number ) )
yield from self . slot_enable_nio ( slot_number , port_number )
adapter . add_nio ( port_number , nio )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_remove_nio_binding ( self , slot_number , port_number ) :
2015-02-10 01:24:13 +00:00
"""
Removes a slot NIO binding .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
: returns : removed NIO instance
"""
try :
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
2015-03-27 15:20:31 +00:00
2015-03-29 00:09:53 +00:00
if adapter is None :
raise DynamipsError ( " Adapter is missing in slot {slot_number} " . format ( slot_number = slot_number ) )
2015-03-27 15:20:31 +00:00
2015-02-13 22:11:14 +00:00
if not adapter . port_exists ( port_number ) :
raise DynamipsError ( " Port {port_number} does not exist in adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
yield from self . slot_disable_nio ( slot_number , port_number )
yield from self . _hypervisor . send ( ' vm slot_remove_nio_binding " {name} " {slot_number} {port_number} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ) )
nio = adapter . get_nio ( port_number )
2015-03-11 16:53:09 +00:00
if nio is None :
return
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-13 22:11:14 +00:00
adapter . remove_nio ( port_number )
log . info ( ' Router " {name} " [ {id} ]: NIO {nio_name} removed from port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio_name = nio . name ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
return nio
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_enable_nio ( self , slot_number , port_number ) :
2015-02-10 01:24:13 +00:00
"""
Enables a slot NIO binding .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
"""
is_running = yield from self . is_running ( )
if is_running : # running router
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_enable_nio " {name} " {slot_number} {port_number} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: NIO enabled on port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-13 13:43:28 +00:00
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def slot_disable_nio ( self , slot_number , port_number ) :
2015-02-10 01:24:13 +00:00
"""
Disables a slot NIO binding .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
"""
is_running = yield from self . is_running ( )
if is_running : # running router
2015-02-13 22:11:14 +00:00
yield from self . _hypervisor . send ( ' vm slot_disable_nio " {name} " {slot_number} {port_number} ' . format ( name = self . _name ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: NIO disabled on port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def start_capture ( self , slot_number , port_number , output_file , data_link_type = " DLT_EN10MB " ) :
2015-02-10 01:24:13 +00:00
"""
Starts a packet capture .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
: param output_file : PCAP destination file for the capture
: param data_link_type : PCAP data link type ( DLT_ * ) , default is DLT_EN10MB
"""
2016-05-16 16:45:03 +00:00
try :
open ( output_file , ' w+ ' ) . close ( )
except OSError as e :
raise DynamipsError ( ' Can not write capture to " {} " : {} ' . format ( output_file , str ( e ) ) )
2015-02-10 01:24:13 +00:00
try :
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
if not adapter . port_exists ( port_number ) :
raise DynamipsError ( " Port {port_number} does not exist in adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
data_link_type = data_link_type . lower ( )
if data_link_type . startswith ( " dlt_ " ) :
data_link_type = data_link_type [ 4 : ]
2015-02-13 22:11:14 +00:00
nio = adapter . get_nio ( port_number )
2015-02-10 01:24:13 +00:00
2015-06-01 21:42:17 +00:00
if not nio :
raise DynamipsError ( " Port {slot_number} / {port_number} is not connected " . format ( slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
if nio . input_filter [ 0 ] is not None and nio . output_filter [ 0 ] is not None :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( " Port {port_number} has already a filter applied on {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
yield from nio . bind_filter ( " both " , " capture " )
yield from nio . setup_filter ( " both " , ' {} " {} " ' . format ( data_link_type , output_file ) )
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: starting packet capture on port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio_name = nio . name ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-13 13:43:28 +00:00
2015-02-10 01:24:13 +00:00
@asyncio.coroutine
2015-02-13 22:11:14 +00:00
def stop_capture ( self , slot_number , port_number ) :
2015-02-10 01:24:13 +00:00
"""
Stops a packet capture .
2015-02-13 22:11:14 +00:00
: param slot_number : slot number
: param port_number : port number
2015-02-10 01:24:13 +00:00
"""
try :
2015-02-13 22:11:14 +00:00
adapter = self . _slots [ slot_number ]
2015-02-10 01:24:13 +00:00
except IndexError :
2015-02-13 22:11:14 +00:00
raise DynamipsError ( ' Slot {slot_number} does not exist on router " {name} " ' . format ( name = self . _name ,
slot_number = slot_number ) )
if not adapter . port_exists ( port_number ) :
raise DynamipsError ( " Port {port_number} does not exist in adapter {adapter} " . format ( adapter = adapter ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
2015-02-13 22:11:14 +00:00
nio = adapter . get_nio ( port_number )
2015-06-01 21:42:17 +00:00
if not nio :
raise DynamipsError ( " Port {slot_number} / {port_number} is not connected " . format ( slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
yield from nio . unbind_filter ( " both " )
2015-02-13 22:11:14 +00:00
log . info ( ' Router " {name} " [ {id} ]: stopping packet capture on port {slot_number} / {port_number} ' . format ( name = self . _name ,
id = self . _id ,
nio_name = nio . name ,
slot_number = slot_number ,
port_number = port_number ) )
2015-02-10 01:24:13 +00:00
def _create_slots ( self , numslots ) :
"""
Creates the appropriate number of slots for this router .
: param numslots : number of slots to create
"""
self . _slots = numslots * [ None ]
@property
def slots ( self ) :
"""
Returns the slots for this router .
: return : slot list
"""
return self . _slots
@property
def startup_config ( self ) :
"""
Returns the startup - config for this router .
: returns : path to startup - config file
"""
return self . _startup_config
@property
def private_config ( self ) :
"""
Returns the private - config for this router .
: returns : path to private - config file
"""
return self . _private_config
2015-02-16 23:53:50 +00:00
@asyncio.coroutine
def set_name ( self , new_name ) :
"""
Renames this router .
: param new_name : new name string
"""
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
if self . _startup_config :
# change the hostname in the startup-config
startup_config_path = os . path . join ( module_workdir , " configs " , " i {} _startup-config.cfg " . format ( self . _dynamips_id ) )
if os . path . isfile ( startup_config_path ) :
try :
2015-04-25 17:58:34 +00:00
with open ( startup_config_path , " r+ " , encoding = " utf-8 " , errors = " replace " ) as f :
2015-02-16 23:53:50 +00:00
old_config = f . read ( )
new_config = old_config . replace ( self . name , new_name )
f . seek ( 0 )
f . write ( new_config )
except OSError as e :
raise DynamipsError ( " Could not amend the configuration {} : {} " . format ( startup_config_path , e ) )
if self . _private_config :
# change the hostname in the private-config
private_config_path = os . path . join ( module_workdir , " configs " , " i {} _private-config.cfg " . format ( self . _dynamips_id ) )
if os . path . isfile ( private_config_path ) :
try :
2015-04-25 17:58:34 +00:00
with open ( private_config_path , " r+ " , encoding = " utf-8 " , errors = " replace " ) as f :
2015-02-16 23:53:50 +00:00
old_config = f . read ( )
new_config = old_config . replace ( self . name , new_name )
f . seek ( 0 )
f . write ( new_config )
except OSError as e :
raise DynamipsError ( " Could not amend the configuration {} : {} " . format ( private_config_path , e ) )
yield from self . _hypervisor . send ( ' vm rename " {name} " " {new_name} " ' . format ( name = self . _name , new_name = new_name ) )
log . info ( ' Router " {name} " [ {id} ]: renamed to " {new_name} " ' . format ( name = self . _name , id = self . _id , new_name = new_name ) )
self . _name = new_name
@asyncio.coroutine
2015-03-08 20:13:19 +00:00
def set_configs ( self , startup_config , private_config = ' ' ) :
2015-02-16 23:53:50 +00:00
"""
Sets the config files that are pushed to startup - config and
private - config in NVRAM when the instance is started .
: param startup_config : path to statup - config file
: param private_config : path to private - config file
( keep existing data when if an empty string )
"""
startup_config = startup_config . replace ( " \\ " , ' / ' )
private_config = private_config . replace ( " \\ " , ' / ' )
if self . _startup_config != startup_config or self . _private_config != private_config :
2015-05-05 00:42:32 +00:00
self . _startup_config = startup_config
self . _private_config = private_config
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
private_config_path = os . path . join ( module_workdir , private_config )
try :
if not os . path . getsize ( private_config_path ) :
# an empty private-config can prevent a router to boot.
private_config = ' '
except OSError as e :
raise DynamipsError ( " Cannot access the private-config {} : {} " . format ( private_config_path , e ) )
2015-02-16 23:53:50 +00:00
yield from self . _hypervisor . send ( ' vm set_config " {name} " " {startup} " " {private} " ' . format ( name = self . _name ,
startup = startup_config ,
private = private_config ) )
log . info ( ' Router " {name} " [ {id} ]: has a new startup-config set: " {startup} " ' . format ( name = self . _name ,
id = self . _id ,
startup = startup_config ) )
if private_config :
log . info ( ' Router " {name} " [ {id} ]: has a new private-config set: " {private} " ' . format ( name = self . _name ,
id = self . _id ,
private = private_config ) )
@asyncio.coroutine
def extract_config ( self ) :
"""
Gets the contents of the config files
startup - config and private - config from NVRAM .
: returns : tuple ( startup - config , private - config ) base64 encoded
"""
try :
2015-02-22 00:24:39 +00:00
reply = yield from self . _hypervisor . send ( ' vm extract_config " {} " ' . format ( self . _name ) )
2015-02-19 00:48:02 +00:00
except DynamipsError :
2015-02-19 10:33:25 +00:00
# for some reason Dynamips gets frozen when it does not find the magic number in the NVRAM file.
2015-02-16 23:53:50 +00:00
return None , None
2015-02-19 00:48:02 +00:00
reply = reply [ 0 ] . rsplit ( ' ' , 2 ) [ - 2 : ]
2015-02-16 23:53:50 +00:00
startup_config = reply [ 0 ] [ 1 : - 1 ] # get statup-config and remove single quotes
private_config = reply [ 1 ] [ 1 : - 1 ] # get private-config and remove single quotes
return startup_config , private_config
@asyncio.coroutine
def save_configs ( self ) :
"""
Saves the startup - config and private - config to files .
"""
if self . startup_config or self . private_config :
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
startup_config_base64 , private_config_base64 = yield from self . extract_config ( )
if startup_config_base64 :
2015-06-05 14:23:52 +00:00
if not self . startup_config :
self . _startup_config = os . path . join ( " configs " , " i {} _startup-config.cfg " . format ( self . _dynamips_id ) )
2015-02-16 23:53:50 +00:00
try :
2015-04-25 17:58:34 +00:00
config = base64 . b64decode ( startup_config_base64 ) . decode ( " utf-8 " , errors = " replace " )
2015-02-16 23:53:50 +00:00
config = " ! \n " + config . replace ( " \r " , " " )
config_path = os . path . join ( module_workdir , self . startup_config )
2015-04-03 10:13:07 +00:00
with open ( config_path , " wb " ) as f :
2015-02-16 23:53:50 +00:00
log . info ( " saving startup-config to {} " . format ( self . startup_config ) )
2015-04-03 10:13:07 +00:00
f . write ( config . encode ( " utf-8 " ) )
2015-03-29 00:09:53 +00:00
except ( binascii . Error , OSError ) as e :
2015-02-16 23:53:50 +00:00
raise DynamipsError ( " Could not save the startup configuration {} : {} " . format ( config_path , e ) )
2016-05-22 00:58:28 +00:00
if private_config_base64 and base64 . b64decode ( private_config_base64 ) != b ' \n kerberos password \n end \n ' :
2015-06-05 14:23:52 +00:00
if not self . private_config :
self . _private_config = os . path . join ( " configs " , " i {} _private-config.cfg " . format ( self . _dynamips_id ) )
2015-02-16 23:53:50 +00:00
try :
2015-04-25 17:58:34 +00:00
config = base64 . b64decode ( private_config_base64 ) . decode ( " utf-8 " , errors = " replace " )
2015-02-16 23:53:50 +00:00
config_path = os . path . join ( module_workdir , self . private_config )
2015-04-03 10:13:07 +00:00
with open ( config_path , " wb " ) as f :
2015-02-16 23:53:50 +00:00
log . info ( " saving private-config to {} " . format ( self . private_config ) )
2015-04-03 10:13:07 +00:00
f . write ( config . encode ( " utf-8 " ) )
2015-03-29 00:09:53 +00:00
except ( binascii . Error , OSError ) as e :
2015-02-16 23:53:50 +00:00
raise DynamipsError ( " Could not save the private configuration {} : {} " . format ( config_path , e ) )
2015-02-10 01:24:13 +00:00
2015-02-16 05:13:24 +00:00
def delete ( self ) :
"""
2016-05-11 17:35:36 +00:00
Delete this VM ( including all its files ) .
2015-02-16 05:13:24 +00:00
"""
# delete the VM files
project_dir = os . path . join ( self . project . module_working_directory ( self . manager . module_name . lower ( ) ) )
files = glob . glob ( os . path . join ( project_dir , " {} _i {} * " . format ( self . _platform , self . _dynamips_id ) ) )
2015-02-16 23:53:50 +00:00
module_workdir = self . project . module_working_directory ( self . manager . module_name . lower ( ) )
# delete the startup-config
if self . _startup_config :
startup_config_path = os . path . join ( module_workdir , " configs " , " i {} _startup-config.cfg " . format ( self . _dynamips_id ) )
if os . path . isfile ( startup_config_path ) :
files . append ( startup_config_path )
# delete the private-config
if self . _private_config :
private_config_path = os . path . join ( module_workdir , " configs " , " i {} _private-config.cfg " . format ( self . _dynamips_id ) )
if os . path . isfile ( private_config_path ) :
files . append ( private_config_path )
2015-02-16 05:13:24 +00:00
for file in files :
try :
log . debug ( " Deleting file {} " . format ( file ) )
yield from wait_run_in_executor ( os . remove , file )
except OSError as e :
log . warn ( " Could not delete file {} : {} " . format ( file , e ) )
continue
2015-10-07 09:34:27 +00:00
self . manager . release_dynamips_id ( self . _project . id , self . _dynamips_id )
2015-09-22 12:39:21 +00:00
2015-02-16 05:13:24 +00:00
@asyncio.coroutine
2015-02-17 01:21:10 +00:00
def clean_delete ( self ) :
2015-02-16 05:13:24 +00:00
"""
Deletes this router & associated files ( nvram , disks etc . )
"""
yield from self . _hypervisor . send ( ' vm clean_delete " {} " ' . format ( self . _name ) )
self . _hypervisor . devices . remove ( self )
log . info ( ' Router " {name} " [ {id} ] has been deleted (including associated files) ' . format ( name = self . _name , id = self . _id ) )
2016-06-13 13:52:31 +00:00
def _memory_files ( self ) :
project_dir = os . path . join ( self . project . module_working_directory ( self . manager . module_name . lower ( ) ) )
return [
os . path . join ( project_dir , " {} _i {} _rom " . format ( self . platform , self . dynamips_id ) ) ,
os . path . join ( project_dir , " {} _i {} _nvram " . format ( self . platform , self . dynamips_id ) )
]