2015-05-01 04:05:37 +03:00
# -*- coding: utf-8 -*-
#
2015-09-05 23:38:11 +03:00
# Copyright (C) 2015 GNS3 Technologies Inc.
2015-05-01 04:05:37 +03:00
#
# 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/>.
"""
VMware VM instance .
"""
import sys
import os
2015-05-28 06:06:18 +03:00
import socket
2015-05-01 04:05:37 +03:00
import asyncio
2015-05-28 06:06:18 +03:00
import tempfile
2015-05-01 04:05:37 +03:00
2015-05-28 06:06:18 +03:00
from gns3server . utils . telnet_server import TelnetServer
2015-07-03 05:10:20 +03:00
from gns3server . utils . interfaces import get_windows_interfaces
2015-05-22 06:48:59 +03:00
from collections import OrderedDict
2015-05-01 04:05:37 +03:00
from . vmware_error import VMwareError
from . . nios . nio_udp import NIOUDP
2015-09-05 23:38:11 +03:00
from . nio_vmnet import NIOVMNET
2015-05-15 05:11:57 +03:00
from . . adapters . ethernet_adapter import EthernetAdapter
2015-05-01 04:05:37 +03:00
from . . base_vm import BaseVM
2015-05-28 06:06:18 +03:00
if sys . platform . startswith ( ' win ' ) :
import msvcrt
import win32file
2015-05-21 04:05:26 +03:00
2015-05-01 04:05:37 +03:00
import logging
log = logging . getLogger ( __name__ )
2015-05-21 04:05:26 +03:00
2015-05-01 04:05:37 +03:00
class VMwareVM ( BaseVM ) :
"""
VMware VM implementation .
"""
def __init__ ( self , name , vm_id , project , manager , vmx_path , linked_clone , console = None ) :
super ( ) . __init__ ( name , vm_id , project , manager , console = console )
self . _linked_clone = linked_clone
2015-05-22 06:48:59 +03:00
self . _vmx_pairs = OrderedDict ( )
2015-05-28 06:06:18 +03:00
self . _telnet_server_thread = None
self . _serial_pipe = None
2015-05-22 06:48:59 +03:00
self . _vmnets = [ ]
self . _maximum_adapters = 10
2015-05-28 06:06:18 +03:00
self . _started = False
2015-05-01 04:05:37 +03:00
self . _closed = False
# VMware VM settings
self . _headless = False
self . _vmx_path = vmx_path
2015-05-15 05:11:57 +03:00
self . _enable_remote_console = False
2015-06-19 00:02:31 +03:00
self . _acpi_shutdown = False
2015-05-15 05:11:57 +03:00
self . _adapters = 0
self . _ethernet_adapters = { }
self . _adapter_type = " e1000 "
2015-09-05 23:38:11 +03:00
self . _use_ubridge = True
2015-05-22 06:48:59 +03:00
self . _use_any_adapter = False
2015-05-15 05:11:57 +03:00
if not os . path . exists ( vmx_path ) :
2015-06-26 18:09:19 +03:00
raise VMwareError ( ' VMware VM " {name} " [ {id} ]: could not find VMX file " {vmx_path} " ' . format ( name = name , id = vm_id , vmx_path = vmx_path ) )
2015-05-01 04:05:37 +03:00
def __json__ ( self ) :
2015-06-26 18:09:19 +03:00
json = { " name " : self . name ,
2015-05-01 04:05:37 +03:00
" vm_id " : self . id ,
" console " : self . console ,
" project_id " : self . project . id ,
" vmx_path " : self . vmx_path ,
2015-05-15 05:11:57 +03:00
" headless " : self . headless ,
2015-06-19 00:02:31 +03:00
" acpi_shutdown " : self . acpi_shutdown ,
2015-05-15 05:11:57 +03:00
" enable_remote_console " : self . enable_remote_console ,
" adapters " : self . _adapters ,
2015-05-22 06:48:59 +03:00
" adapter_type " : self . adapter_type ,
2015-09-05 23:38:11 +03:00
" use_ubridge " : self . use_ubridge ,
2015-07-09 02:48:34 +03:00
" use_any_adapter " : self . use_any_adapter ,
" vm_directory " : self . working_dir }
2015-06-26 18:09:19 +03:00
return json
2015-05-22 06:48:59 +03:00
@property
def vmnets ( self ) :
return self . _vmnets
2015-05-01 04:05:37 +03:00
@asyncio.coroutine
def _control_vm ( self , subcommand , * additional_args ) :
args = [ self . _vmx_path ]
args . extend ( additional_args )
result = yield from self . manager . execute ( subcommand , args )
log . debug ( " Control VM ' {} ' result: {} " . format ( subcommand , result ) )
return result
2015-09-05 23:38:11 +03:00
def _read_vmx_file ( self ) :
"""
Reads from the VMware VMX file corresponding to this VM .
"""
try :
self . _vmx_pairs = self . manager . parse_vmware_file ( self . _vmx_path )
except OSError as e :
raise VMwareError ( ' Could not read VMware VMX file " {} " : {} ' . format ( self . _vmx_path , e ) )
def _write_vmx_file ( self ) :
"""
Writes pairs to the VMware VMX file corresponding to this VM .
"""
try :
self . manager . write_vmx_file ( self . _vmx_path , self . _vmx_pairs )
except OSError as e :
raise VMwareError ( ' Could not write VMware VMX file " {} " : {} ' . format ( self . _vmx_path , e ) )
2015-05-31 05:26:38 +03:00
@asyncio.coroutine
def create ( self ) :
2015-06-18 02:36:52 +03:00
"""
Creates this VM and handle linked clones .
"""
2015-05-31 05:26:38 +03:00
if self . _linked_clone and not os . path . exists ( os . path . join ( self . working_dir , os . path . basename ( self . _vmx_path ) ) ) :
# create the base snapshot for linked clones
base_snapshot_name = " GNS3 Linked Base for clones "
vmsd_path = os . path . splitext ( self . _vmx_path ) [ 0 ] + " .vmsd "
if not os . path . exists ( vmsd_path ) :
raise VMwareError ( " {} doesn ' t not exist " . format ( vmsd_path ) )
try :
vmsd_pairs = self . manager . parse_vmware_file ( vmsd_path )
except OSError as e :
raise VMwareError ( ' Could not read VMware VMSD file " {} " : {} ' . format ( vmsd_path , e ) )
gns3_snapshot_exists = False
for value in vmsd_pairs . values ( ) :
if value == base_snapshot_name :
gns3_snapshot_exists = True
break
if not gns3_snapshot_exists :
log . info ( " Creating snapshot ' {} ' " . format ( base_snapshot_name ) )
yield from self . _control_vm ( " snapshot " , base_snapshot_name )
# create the linked clone based on the base snapshot
new_vmx_path = os . path . join ( self . working_dir , self . name + " .vmx " )
yield from self . _control_vm ( " clone " ,
new_vmx_path ,
" linked " ,
" -snapshot= {} " . format ( base_snapshot_name ) ,
" -cloneName= {} " . format ( self . name ) )
try :
vmsd_pairs = self . manager . parse_vmware_file ( vmsd_path )
except OSError as e :
raise VMwareError ( ' Could not read VMware VMSD file " {} " : {} ' . format ( vmsd_path , e ) )
snapshot_name = None
for name , value in vmsd_pairs . items ( ) :
if value == base_snapshot_name :
snapshot_name = name . split ( " . " , 1 ) [ 0 ]
break
if snapshot_name is None :
raise VMwareError ( " Could not find the linked base snapshot in {} " . format ( vmsd_path ) )
num_clones_entry = " {} .numClones " . format ( snapshot_name )
if num_clones_entry in vmsd_pairs :
try :
nb_of_clones = int ( vmsd_pairs [ num_clones_entry ] )
except ValueError :
raise VMwareError ( " Value of {} in {} is not a number " . format ( num_clones_entry , vmsd_path ) )
vmsd_pairs [ num_clones_entry ] = str ( nb_of_clones - 1 )
for clone_nb in range ( 0 , nb_of_clones ) :
clone_entry = " {} .clone {} " . format ( snapshot_name , clone_nb )
if clone_entry in vmsd_pairs :
del vmsd_pairs [ clone_entry ]
try :
self . manager . write_vmware_file ( vmsd_path , vmsd_pairs )
except OSError as e :
raise VMwareError ( ' Could not write VMware VMSD file " {} " : {} ' . format ( vmsd_path , e ) )
# update the VMX file path
self . _vmx_path = new_vmx_path
2015-05-22 06:48:59 +03:00
def _get_vmx_setting ( self , name , value = None ) :
2015-05-21 04:05:26 +03:00
2015-05-22 06:48:59 +03:00
if name in self . _vmx_pairs :
if value is not None :
if self . _vmx_pairs [ name ] == value :
return value
else :
return self . _vmx_pairs [ name ]
return None
2015-05-21 04:05:26 +03:00
2015-05-15 05:11:57 +03:00
def _set_network_options ( self ) :
2015-06-18 02:36:52 +03:00
"""
Set up VMware networking .
"""
2015-05-15 05:11:57 +03:00
2015-09-05 23:38:11 +03:00
# first some sanity checks
2015-05-21 04:05:26 +03:00
for adapter_number in range ( 0 , self . _adapters ) :
2015-09-05 23:38:11 +03:00
# we want the vmnet interface to be connected when starting the VM
2015-05-22 06:48:59 +03:00
connected = " ethernet {} .startConnected " . format ( adapter_number )
if self . _get_vmx_setting ( connected ) :
del self . _vmx_pairs [ connected ]
# check for adapter type
if self . _adapter_type != " default " :
2015-06-19 02:53:08 +03:00
adapter_type = " ethernet {} .virtualdev " . format ( adapter_number )
2015-05-22 06:48:59 +03:00
if adapter_type in self . _vmx_pairs and self . _vmx_pairs [ adapter_type ] != self . _adapter_type :
2015-09-05 23:38:11 +03:00
raise VMwareError ( " Existing VMware network adapter {} is not of type {} , please fix or set adapter type to default in GNS3 " . format ( adapter_number ,
self . _adapter_type ) )
# # check if any vmnet interface managed by GNS3 is being used on existing VMware adapters
# if self._get_vmx_setting("ethernet{}.present".format(adapter_number), "TRUE"):
# connection_type = "ethernet{}.connectiontype".format(adapter_number)
# if connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("hostonly", "custom"):
# vnet = "ethernet{}.vnet".format(adapter_number)
# if vnet in self._vmx_pairs:
# vmnet = os.path.basename(self._vmx_pairs[vnet])
# #nio = self._ethernet_adapters[adapter_number].get_nio(0)
# if self.manager.is_managed_vmnet(vmnet):
# raise VMwareError("Network adapter {} is already associated with VMnet interface {} which is managed by GNS3, please remove".format(adapter_number, vmnet))
# then configure VMware network adapters
2015-05-22 06:48:59 +03:00
self . manager . refresh_vmnet_list ( )
for adapter_number in range ( 0 , self . _adapters ) :
2015-09-05 23:38:11 +03:00
# add/update the interface
2015-05-22 06:48:59 +03:00
ethernet_adapter = { " ethernet {} .present " . format ( adapter_number ) : " TRUE " ,
2015-06-19 02:53:08 +03:00
" ethernet {} .addresstype " . format ( adapter_number ) : " generated " ,
" ethernet {} .generatedaddressoffset " . format ( adapter_number ) : " 0 " }
2015-05-22 06:48:59 +03:00
self . _vmx_pairs . update ( ethernet_adapter )
if self . _adapter_type != " default " :
2015-06-19 02:53:08 +03:00
self . _vmx_pairs [ " ethernet {} .virtualdev " . format ( adapter_number ) ] = self . _adapter_type
2015-05-22 06:48:59 +03:00
2015-06-19 02:53:08 +03:00
connection_type = " ethernet {} .connectiontype " . format ( adapter_number )
2015-05-22 06:48:59 +03:00
if not self . _use_any_adapter and connection_type in self . _vmx_pairs and self . _vmx_pairs [ connection_type ] in ( " nat " , " bridged " , " hostonly " ) :
continue
2015-06-19 02:53:08 +03:00
self . _vmx_pairs [ " ethernet {} .connectiontype " . format ( adapter_number ) ] = " custom "
2015-09-05 23:38:11 +03:00
if self . _use_ubridge :
# make sure we have a vmnet per adapter if we use uBridge
allocate_vmnet = False
# first check if a vmnet is already assigned to the adapter
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet in self . _vmx_pairs :
vmnet = os . path . basename ( self . _vmx_pairs [ vnet ] )
2015-09-17 16:46:06 +03:00
if self . manager . is_managed_vmnet ( vmnet ) :
2015-09-05 23:38:11 +03:00
# vmnet already managed, try to allocate a new one
allocate_vmnet = True
else :
# otherwise allocate a new one
allocate_vmnet = True
if allocate_vmnet :
try :
vmnet = self . manager . allocate_vmnet ( )
except :
# clear everything up in case of error (e.g. no enough vmnets)
self . _vmnets . clear ( )
raise
# mark the vmnet managed by us
2015-09-17 16:46:06 +03:00
if vmnet not in self . _vmnets :
2015-09-05 23:38:11 +03:00
self . _vmnets . append ( vmnet )
self . _vmx_pairs [ " ethernet {} .vnet " . format ( adapter_number ) ] = vmnet
2015-09-13 23:52:25 +03:00
else :
# not connected to anything...
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet not in self . _vmx_pairs :
self . _vmx_pairs [ " ethernet {} .startconnected " . format ( adapter_number ) ] = " FALSE "
2015-05-22 06:48:59 +03:00
# disable remaining network adapters
for adapter_number in range ( self . _adapters , self . _maximum_adapters ) :
if self . _get_vmx_setting ( " ethernet {} .present " . format ( adapter_number ) , " TRUE " ) :
log . debug ( " disabling remaining adapter {} " . format ( adapter_number ) )
2015-06-19 02:53:08 +03:00
self . _vmx_pairs [ " ethernet {} .startconnected " . format ( adapter_number ) ] = " FALSE "
2015-05-21 04:05:26 +03:00
2015-07-20 07:55:10 +03:00
@asyncio.coroutine
def _add_ubridge_connection ( self , nio , adapter_number ) :
"""
Creates a connection in uBridge .
2015-05-21 04:05:26 +03:00
2015-07-20 07:55:10 +03:00
: param nio : NIO instance
: param adapter_number : adapter number
2015-05-21 04:05:26 +03:00
"""
2015-07-20 07:55:10 +03:00
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet not in self . _vmx_pairs :
raise VMwareError ( " vnet {} not in VMX file " . format ( vnet ) )
yield from self . _ubridge_hypervisor . send ( " bridge create {name} " . format ( name = vnet ) )
vmnet_interface = os . path . basename ( self . _vmx_pairs [ vnet ] )
if sys . platform . startswith ( " linux " ) :
yield from self . _ubridge_hypervisor . send ( ' bridge add_nio_linux_raw {name} " {interface} " ' . format ( name = vnet ,
interface = vmnet_interface ) )
elif sys . platform . startswith ( " win " ) :
windows_interfaces = get_windows_interfaces ( )
npf = None
for interface in windows_interfaces :
if " netcard " in interface and vmnet_interface in interface [ " netcard " ] :
npf = interface [ " id " ]
elif vmnet_interface in interface [ " name " ] :
npf = interface [ " id " ]
if npf :
yield from self . _ubridge_hypervisor . send ( ' bridge add_nio_ethernet {name} " {interface} " ' . format ( name = vnet ,
interface = npf ) )
else :
raise VMwareError ( " Could not find NPF id for VMnet interface {} " . format ( vmnet_interface ) )
2015-09-10 12:44:56 +03:00
elif sys . platform . startswith ( " darwin " ) :
yield from self . _ubridge_hypervisor . send ( ' bridge add_nio_fusion_vmnet {name} " {interface} " ' . format ( name = vnet ,
interface = vmnet_interface ) )
2015-07-20 07:55:10 +03:00
else :
yield from self . _ubridge_hypervisor . send ( ' bridge add_nio_ethernet {name} " {interface} " ' . format ( name = vnet ,
interface = vmnet_interface ) )
if isinstance ( nio , NIOUDP ) :
yield from self . _ubridge_hypervisor . send ( ' bridge add_nio_udp {name} {lport} {rhost} {rport} ' . format ( name = vnet ,
lport = nio . lport ,
rhost = nio . rhost ,
rport = nio . rport ) )
if nio . capturing :
yield from self . _ubridge_hypervisor . send ( ' bridge start_capture {name} " {pcap_file} " ' . format ( name = vnet ,
pcap_file = nio . pcap_output_file ) )
yield from self . _ubridge_hypervisor . send ( ' bridge start {name} ' . format ( name = vnet ) )
@asyncio.coroutine
def _delete_ubridge_connection ( self , adapter_number ) :
2015-05-21 04:05:26 +03:00
"""
2015-07-20 07:55:10 +03:00
Deletes a connection in uBridge .
2015-05-15 05:11:57 +03:00
2015-07-20 07:55:10 +03:00
: param adapter_number : adapter number
"""
2015-05-21 04:05:26 +03:00
2015-07-20 07:55:10 +03:00
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet not in self . _vmx_pairs :
raise VMwareError ( " vnet {} not in VMX file " . format ( vnet ) )
yield from self . _ubridge_hypervisor . send ( " bridge delete {name} " . format ( name = vnet ) )
2015-05-21 04:05:26 +03:00
2015-09-13 18:40:09 +03:00
@asyncio.coroutine
def _start_ubridge_capture ( self , adapter_number , output_file ) :
"""
Start a packet capture in uBridge .
: param adapter_number : adapter number
: param output_file : PCAP destination file for the capture
"""
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet not in self . _vmx_pairs :
raise VMwareError ( " vnet {} not in VMX file " . format ( vnet ) )
yield from self . _ubridge_hypervisor . send ( ' bridge start_capture {name} " {output_file} " ' . format ( name = vnet ,
output_file = output_file ) )
@asyncio.coroutine
def _stop_ubridge_capture ( self , adapter_number ) :
"""
Stop a packet capture in uBridge .
: param adapter_number : adapter number
"""
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet not in self . _vmx_pairs :
raise VMwareError ( " vnet {} not in VMX file " . format ( vnet ) )
yield from self . _ubridge_hypervisor . send ( " bridge stop_capture {name} " . format ( name = vnet ) )
2015-07-27 04:21:30 +03:00
def check_hw_virtualization ( self ) :
"""
Returns either hardware virtualization is activated or not .
: returns : boolean
"""
2015-09-05 23:38:11 +03:00
self . _read_vmx_file ( )
2015-07-27 04:21:30 +03:00
if self . _get_vmx_setting ( " vhv.enable " , " TRUE " ) :
return True
return False
2015-05-01 04:05:37 +03:00
@asyncio.coroutine
def start ( self ) :
"""
Starts this VMware VM .
"""
2015-09-08 22:30:22 +03:00
# The lock dection is not working on OSX the .lck is never deleted
if os . path . exists ( self . _vmx_path + " .lck " ) and not sys . platform . startswith ( " darwin " ) :
2015-05-26 00:49:28 +03:00
raise VMwareError ( " VM locked, it is either running or being edited in VMware " )
2015-05-21 04:05:26 +03:00
ubridge_path = self . ubridge_path
if not ubridge_path or not os . path . isfile ( ubridge_path ) :
raise VMwareError ( " ubridge is necessary to start a VMware VM " )
2015-09-05 23:38:11 +03:00
if self . _use_ubridge :
yield from self . _start_ubridge ( )
2015-05-28 06:06:18 +03:00
2015-09-05 23:38:11 +03:00
self . _read_vmx_file ( )
2015-05-15 05:11:57 +03:00
self . _set_network_options ( )
2015-05-28 06:06:18 +03:00
self . _set_serial_console ( )
2015-09-05 23:38:11 +03:00
self . _write_vmx_file ( )
2015-05-21 04:05:26 +03:00
2015-05-01 04:05:37 +03:00
if self . _headless :
yield from self . _control_vm ( " start " , " nogui " )
else :
yield from self . _control_vm ( " start " )
2015-05-28 06:06:18 +03:00
2015-09-05 23:38:11 +03:00
if self . _use_ubridge :
for adapter_number in range ( 0 , self . _adapters ) :
nio = self . _ethernet_adapters [ adapter_number ] . get_nio ( 0 )
if nio :
yield from self . _add_ubridge_connection ( nio , adapter_number )
2015-07-20 07:55:10 +03:00
2015-05-28 06:06:18 +03:00
if self . _enable_remote_console and self . _console is not None :
yield from asyncio . sleep ( 1 ) # give some time to VMware to create the pipe file.
self . _start_remote_console ( )
2015-07-22 07:58:28 +03:00
if self . _get_vmx_setting ( " vhv.enable " , " TRUE " ) :
self . _hw_virtualization = True
2015-05-28 06:06:18 +03:00
self . _started = True
2015-05-01 04:05:37 +03:00
log . info ( " VMware VM ' {name} ' [ {id} ] started " . format ( name = self . name , id = self . id ) )
@asyncio.coroutine
def stop ( self ) :
"""
Stops this VMware VM .
"""
2015-07-22 07:58:28 +03:00
self . _hw_virtualization = False
2015-05-28 06:06:18 +03:00
self . _stop_remote_console ( )
2015-07-20 07:55:10 +03:00
if self . _ubridge_hypervisor and self . _ubridge_hypervisor . is_running ( ) :
yield from self . _ubridge_hypervisor . stop ( )
2015-05-21 04:05:26 +03:00
2015-05-22 06:48:59 +03:00
try :
2015-06-19 00:02:31 +03:00
if self . acpi_shutdown :
# use ACPI to shutdown the VM
2015-06-19 02:53:08 +03:00
yield from self . _control_vm ( " stop " , " soft " )
2015-06-19 00:02:31 +03:00
else :
yield from self . _control_vm ( " stop " )
2015-05-22 06:48:59 +03:00
finally :
2015-05-28 06:06:18 +03:00
self . _started = False
2015-05-22 06:48:59 +03:00
2015-09-05 23:38:11 +03:00
self . _read_vmx_file ( )
if self . _use_ubridge :
self . _vmnets . clear ( )
# remove the adapters managed by GNS3
for adapter_number in range ( 0 , self . _adapters ) :
if self . _get_vmx_setting ( " ethernet {} .vnet " . format ( adapter_number ) ) or \
self . _get_vmx_setting ( " ethernet {} .connectiontype " . format ( adapter_number ) ) is None :
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet in self . _vmx_pairs :
vmnet = os . path . basename ( self . _vmx_pairs [ vnet ] )
if not self . manager . is_managed_vmnet ( vmnet ) :
continue
log . debug ( " removing adapter {} " . format ( adapter_number ) )
2015-09-17 16:51:46 +03:00
for key in list ( self . _vmx_pairs . keys ( ) ) :
2015-09-05 23:38:11 +03:00
if key . startswith ( " ethernet {} . " . format ( adapter_number ) ) :
del self . _vmx_pairs [ key ]
2015-05-22 06:48:59 +03:00
# re-enable any remaining network adapters
for adapter_number in range ( self . _adapters , self . _maximum_adapters ) :
if self . _get_vmx_setting ( " ethernet {} .present " . format ( adapter_number ) , " TRUE " ) :
log . debug ( " enabling remaining adapter {} " . format ( adapter_number ) )
2015-06-19 02:53:08 +03:00
self . _vmx_pairs [ " ethernet {} .startconnected " . format ( adapter_number ) ] = " TRUE "
2015-09-05 23:38:11 +03:00
self . _write_vmx_file ( )
2015-05-22 06:48:59 +03:00
2015-05-01 04:05:37 +03:00
log . info ( " VMware VM ' {name} ' [ {id} ] stopped " . format ( name = self . name , id = self . id ) )
@asyncio.coroutine
def suspend ( self ) :
"""
Suspends this VMware VM .
"""
2015-05-16 04:09:48 +03:00
if self . manager . host_type != " ws " :
raise VMwareError ( " Pausing a VM is only supported by VMware Workstation " )
2015-05-02 03:47:46 +03:00
yield from self . _control_vm ( " pause " )
log . info ( " VMware VM ' {name} ' [ {id} ] paused " . format ( name = self . name , id = self . id ) )
2015-05-01 04:05:37 +03:00
@asyncio.coroutine
def resume ( self ) :
"""
Resumes this VMware VM .
"""
2015-05-16 04:09:48 +03:00
if self . manager . host_type != " ws " :
raise VMwareError ( " Unpausing a VM is only supported by VMware Workstation " )
2015-05-02 03:47:46 +03:00
yield from self . _control_vm ( " unpause " )
2015-05-01 04:05:37 +03:00
log . info ( " VMware VM ' {name} ' [ {id} ] resumed " . format ( name = self . name , id = self . id ) )
@asyncio.coroutine
def reload ( self ) :
"""
Reloads this VMware VM .
"""
yield from self . _control_vm ( " reset " )
log . info ( " VMware VM ' {name} ' [ {id} ] reloaded " . format ( name = self . name , id = self . id ) )
@asyncio.coroutine
def close ( self ) :
"""
2015-09-05 23:38:11 +03:00
Closes this VMware VM .
2015-05-01 04:05:37 +03:00
"""
if self . _closed :
# VM is already closed
return
log . debug ( " VMware VM ' {name} ' [ {id} ] is closing " . format ( name = self . name , id = self . id ) )
if self . _console :
self . _manager . port_manager . release_tcp_port ( self . _console , self . _project )
self . _console = None
2015-05-21 04:05:26 +03:00
for adapter in self . _ethernet_adapters . values ( ) :
if adapter is not None :
for nio in adapter . ports . values ( ) :
if nio and isinstance ( nio , NIOUDP ) :
self . manager . port_manager . release_udp_port ( nio . lport , self . _project )
2015-09-05 23:38:11 +03:00
if nio and isinstance ( nio , NIOVMNET ) and nio . vmnet in self . _vmnets :
self . _vmnets . remove ( nio . vmnet )
2015-05-02 03:47:46 +03:00
try :
2015-06-19 00:02:31 +03:00
self . acpi_shutdown = False
2015-05-02 03:47:46 +03:00
yield from self . stop ( )
except VMwareError :
pass
2015-05-01 04:05:37 +03:00
2015-05-31 05:26:38 +03:00
if self . _linked_clone :
# clean the VMware inventory path from this linked clone
inventory_path = self . manager . get_vmware_inventory_path ( )
2015-06-18 02:36:52 +03:00
inventory_pairs = { }
2015-05-31 05:26:38 +03:00
if os . path . exists ( inventory_path ) :
try :
inventory_pairs = self . manager . parse_vmware_file ( inventory_path )
except OSError as e :
log . warning ( ' Could not read VMware inventory file " {} " : {} ' . format ( inventory_path , e ) )
2015-06-18 02:36:52 +03:00
return
2015-05-31 05:26:38 +03:00
vmlist_entry = None
for name , value in inventory_pairs . items ( ) :
if value == self . _vmx_path :
vmlist_entry = name . split ( " . " , 1 ) [ 0 ]
break
if vmlist_entry is not None :
for name in inventory_pairs . keys ( ) :
if name . startswith ( vmlist_entry ) :
del inventory_pairs [ name ]
try :
self . manager . write_vmware_file ( inventory_path , inventory_pairs )
except OSError as e :
raise VMwareError ( ' Could not write VMware inventory file " {} " : {} ' . format ( inventory_path , e ) )
2015-05-01 04:05:37 +03:00
log . info ( " VirtualBox VM ' {name} ' [ {id} ] closed " . format ( name = self . name , id = self . id ) )
self . _closed = True
@property
def headless ( self ) :
"""
Returns either the VM will start in headless mode
: returns : boolean
"""
return self . _headless
@headless.setter
def headless ( self , headless ) :
"""
Sets either the VM will start in headless mode
: param headless : boolean
"""
if headless :
log . info ( " VMware VM ' {name} ' [ {id} ] has enabled the headless mode " . format ( name = self . name , id = self . id ) )
else :
log . info ( " VMware VM ' {name} ' [ {id} ] has disabled the headless mode " . format ( name = self . name , id = self . id ) )
self . _headless = headless
2015-06-19 00:02:31 +03:00
@property
def acpi_shutdown ( self ) :
"""
Returns either the VM will use ACPI shutdown
: returns : boolean
"""
return self . _acpi_shutdown
@acpi_shutdown.setter
def acpi_shutdown ( self , acpi_shutdown ) :
"""
Sets either the VM will use ACPI shutdown
: param acpi_shutdown : boolean
"""
if acpi_shutdown :
log . info ( " VMware VM ' {name} ' [ {id} ] has enabled the ACPI shutdown mode " . format ( name = self . name , id = self . id ) )
else :
log . info ( " VMware VM ' {name} ' [ {id} ] has disabled the ACPI shutdown mode " . format ( name = self . name , id = self . id ) )
self . _acpi_shutdown = acpi_shutdown
2015-05-01 04:05:37 +03:00
@property
def vmx_path ( self ) :
"""
Returns the path to the vmx file .
: returns : VMware vmx file
"""
return self . _vmx_path
@vmx_path.setter
def vmx_path ( self , vmx_path ) :
"""
Sets the path to the vmx file .
: param vmx_path : VMware vmx file
"""
log . info ( " VMware VM ' {name} ' [ {id} ] has set the vmx file path to ' {vmx} ' " . format ( name = self . name , id = self . id , vmx = vmx_path ) )
self . _vmx_path = vmx_path
2015-05-15 05:11:57 +03:00
@property
def enable_remote_console ( self ) :
"""
Returns either the remote console is enabled or not
: returns : boolean
"""
return self . _enable_remote_console
2015-05-16 04:09:48 +03:00
@enable_remote_console.setter
def enable_remote_console ( self , enable_remote_console ) :
2015-05-15 05:11:57 +03:00
"""
Sets either the console is enabled or not
: param enable_remote_console : boolean
"""
if enable_remote_console :
log . info ( " VMware VM ' {name} ' [ {id} ] has enabled the console " . format ( name = self . name , id = self . id ) )
2015-05-28 06:06:18 +03:00
if self . _started :
self . _start_remote_console ( )
2015-05-15 05:11:57 +03:00
else :
log . info ( " VMware VM ' {name} ' [ {id} ] has disabled the console " . format ( name = self . name , id = self . id ) )
2015-05-28 06:06:18 +03:00
self . _stop_remote_console ( )
2015-05-15 05:11:57 +03:00
self . _enable_remote_console = enable_remote_console
@property
def adapters ( self ) :
"""
Returns the number of adapters configured for this VMware VM .
: returns : number of adapters
"""
return self . _adapters
@adapters.setter
def adapters ( self , adapters ) :
"""
Sets the number of Ethernet adapters for this VMware VM instance .
: param adapters : number of adapters
"""
2015-07-20 07:55:10 +03:00
# VMware VMs are limited to 10 adapters
2015-05-15 05:11:57 +03:00
if adapters > 10 :
raise VMwareError ( " Number of adapters above the maximum supported of 10 " )
self . _ethernet_adapters . clear ( )
for adapter_number in range ( 0 , adapters ) :
self . _ethernet_adapters [ adapter_number ] = EthernetAdapter ( )
self . _adapters = len ( self . _ethernet_adapters )
log . info ( " VMware VM ' {name} ' [ {id} ] has changed the number of Ethernet adapters to {adapters} " . format ( name = self . name ,
id = self . id ,
adapters = adapters ) )
@property
def adapter_type ( self ) :
"""
Returns the adapter type for this VMware VM instance .
: returns : adapter type ( string )
"""
return self . _adapter_type
@adapter_type.setter
def adapter_type ( self , adapter_type ) :
"""
Sets the adapter type for this VMware VM instance .
: param adapter_type : adapter type ( string )
"""
self . _adapter_type = adapter_type
log . info ( " VMware VM ' {name} ' [ {id} ]: adapter type changed to {adapter_type} " . format ( name = self . name ,
id = self . id ,
adapter_type = adapter_type ) )
2015-05-16 04:09:48 +03:00
2015-09-05 23:38:11 +03:00
@property
def use_ubridge ( self ) :
"""
Returns either GNS3 can use uBridge for network connections .
: returns : boolean
"""
return self . _use_ubridge
@use_ubridge.setter
def use_ubridge ( self , use_ubridge ) :
"""
Allows GNS3 to use uBridge for network connections .
: param use_ubridge : boolean
"""
if use_ubridge :
log . info ( " VMware VM ' {name} ' [ {id} ] will use uBridge for network connections " . format ( name = self . name , id = self . id ) )
else :
log . info ( " VMware VM ' {name} ' [ {id} ] will not use uBridge for network connections " . format ( name = self . name , id = self . id ) )
self . _use_ubridge = use_ubridge
2015-05-22 06:48:59 +03:00
@property
def use_any_adapter ( self ) :
"""
Returns either GNS3 can use any VMware adapter on this instance .
: returns : boolean
"""
return self . _use_any_adapter
@use_any_adapter.setter
def use_any_adapter ( self , use_any_adapter ) :
"""
Allows GNS3 to use any VMware adapter on this instance .
: param use_any_adapter : boolean
"""
if use_any_adapter :
log . info ( " VMware VM ' {name} ' [ {id} ] is allowed to use any adapter " . format ( name = self . name , id = self . id ) )
else :
log . info ( " VMware VM ' {name} ' [ {id} ] is not allowed to use any adapter " . format ( name = self . name , id = self . id ) )
self . _use_any_adapter = use_any_adapter
2015-07-20 07:55:10 +03:00
@asyncio.coroutine
2015-05-16 04:09:48 +03:00
def adapter_add_nio_binding ( self , adapter_number , nio ) :
"""
Adds an adapter NIO binding .
: param adapter_number : adapter number
: param nio : NIO instance to add to the slot / port
"""
try :
adapter = self . _ethernet_adapters [ adapter_number ]
except IndexError :
raise VMwareError ( " Adapter {adapter_number} doesn ' t exist on VMware VM ' {name} ' " . format ( name = self . name ,
adapter_number = adapter_number ) )
2015-09-05 23:38:11 +03:00
self . _read_vmx_file ( )
# check if trying to connect to a nat, bridged or host-only adapter
if self . _ethernet_adapters [ adapter_number ] . get_nio ( 0 ) and not self . _use_any_adapter :
if self . _get_vmx_setting ( " ethernet {} .present " . format ( adapter_number ) , " TRUE " ) :
# check for the connection type
connection_type = " ethernet {} .connectiontype " . format ( adapter_number )
if connection_type in self . _vmx_pairs and self . _vmx_pairs [ connection_type ] in ( " nat " , " bridged " , " hostonly " ) :
raise VMwareError ( " Attachment ( {} ) already configured on network adapter {} . "
" Please remove it or allow GNS3 to use any adapter. " . format ( self . _vmx_pairs [ connection_type ] ,
adapter_number ) )
if isinstance ( nio , NIOVMNET ) :
if self . _started :
raise VMwareError ( " Sorry, adding a link to a started VMware VM is not supported without uBridge enabled " )
self . _vmx_pairs [ " ethernet {} .vnet " . format ( adapter_number ) ] = nio . vmnet
self . _write_vmx_file ( )
self . _vmnets . append ( nio . vmnet )
2015-05-16 04:09:48 +03:00
adapter . add_nio ( 0 , nio )
2015-09-05 23:38:11 +03:00
if self . _started and self . _use_ubridge :
2015-07-20 07:55:10 +03:00
yield from self . _add_ubridge_connection ( nio , adapter_number )
2015-05-16 04:09:48 +03:00
log . info ( " VMware VM ' {name} ' [ {id} ]: {nio} added to adapter {adapter_number} " . format ( name = self . name ,
id = self . id ,
nio = nio ,
adapter_number = adapter_number ) )
2015-07-20 07:55:10 +03:00
@asyncio.coroutine
2015-05-16 04:09:48 +03:00
def adapter_remove_nio_binding ( self , adapter_number ) :
"""
Removes an adapter NIO binding .
: param adapter_number : adapter number
: returns : NIO instance
"""
try :
adapter = self . _ethernet_adapters [ adapter_number ]
except IndexError :
raise VMwareError ( " Adapter {adapter_number} doesn ' t exist on VMware VM ' {name} ' " . format ( name = self . name ,
adapter_number = adapter_number ) )
nio = adapter . get_nio ( 0 )
if isinstance ( nio , NIOUDP ) :
self . manager . port_manager . release_udp_port ( nio . lport , self . _project )
2015-09-05 23:38:11 +03:00
if isinstance ( nio , NIOVMNET ) :
self . _read_vmx_file ( )
vnet = " ethernet {} .vnet " . format ( adapter_number )
if vnet in self . _vmx_pairs :
del self . _vmx_pairs [ vnet ]
self . _write_vmx_file ( )
if nio . vmnet in self . _vmnets :
self . _vmnets . remove ( nio . vmnet )
2015-05-16 04:09:48 +03:00
adapter . remove_nio ( 0 )
2015-09-05 23:38:11 +03:00
if self . _started and self . _use_ubridge :
2015-07-20 07:55:10 +03:00
yield from self . _delete_ubridge_connection ( adapter_number )
2015-05-16 04:09:48 +03:00
log . info ( " VMware VM ' {name} ' [ {id} ]: {nio} removed from adapter {adapter_number} " . format ( name = self . name ,
id = self . id ,
nio = nio ,
adapter_number = adapter_number ) )
2015-06-18 02:36:52 +03:00
2015-05-16 04:09:48 +03:00
return nio
2015-05-28 06:06:18 +03:00
def _get_pipe_name ( self ) :
"""
Returns the pipe name to create a serial connection .
: returns : pipe path ( string )
"""
if sys . platform . startswith ( " win " ) :
pipe_name = r " \\ . \ pipe \ gns3_vmware \ {} " . format ( self . id )
else :
pipe_name = os . path . join ( tempfile . gettempdir ( ) , " gns3_vmware " , " {} " . format ( self . id ) )
try :
os . makedirs ( os . path . dirname ( pipe_name ) , exist_ok = True )
except OSError as e :
raise VMwareError ( " Could not create the VMware pipe directory: {} " . format ( e ) )
return pipe_name
def _set_serial_console ( self ) :
"""
Configures the first serial port to allow a serial console connection .
"""
pipe_name = self . _get_pipe_name ( )
serial_port = { " serial0.present " : " TRUE " ,
2015-06-19 02:53:08 +03:00
" serial0.filetype " : " pipe " ,
" serial0.filename " : pipe_name ,
" serial0.pipe.endpoint " : " server " }
2015-05-28 06:06:18 +03:00
self . _vmx_pairs . update ( serial_port )
def _start_remote_console ( self ) :
"""
Starts remote console support for this VM .
"""
# starts the Telnet to pipe thread
pipe_name = self . _get_pipe_name ( )
if sys . platform . startswith ( " win " ) :
try :
self . _serial_pipe = open ( pipe_name , " a+b " )
except OSError as e :
raise VMwareError ( " Could not open the pipe {} : {} " . format ( pipe_name , e ) )
try :
self . _telnet_server_thread = TelnetServer ( self . name , msvcrt . get_osfhandle ( self . _serial_pipe . fileno ( ) ) , self . _manager . port_manager . console_host , self . _console )
except OSError as e :
raise VMwareError ( " Unable to create Telnet server: {} " . format ( e ) )
self . _telnet_server_thread . start ( )
else :
try :
self . _serial_pipe = socket . socket ( socket . AF_UNIX , socket . SOCK_STREAM )
self . _serial_pipe . connect ( pipe_name )
except OSError as e :
raise VMwareError ( " Could not connect to the pipe {} : {} " . format ( pipe_name , e ) )
try :
self . _telnet_server_thread = TelnetServer ( self . name , self . _serial_pipe , self . _manager . port_manager . console_host , self . _console )
except OSError as e :
raise VMwareError ( " Unable to create Telnet server: {} " . format ( e ) )
self . _telnet_server_thread . start ( )
def _stop_remote_console ( self ) :
"""
Stops remote console support for this VM .
"""
if self . _telnet_server_thread :
if self . _telnet_server_thread . is_alive ( ) :
self . _telnet_server_thread . stop ( )
self . _telnet_server_thread . join ( timeout = 3 )
if self . _telnet_server_thread . is_alive ( ) :
log . warn ( " Serial pipe thread is still alive! " )
self . _telnet_server_thread = None
if self . _serial_pipe :
if sys . platform . startswith ( " win " ) :
win32file . CloseHandle ( msvcrt . get_osfhandle ( self . _serial_pipe . fileno ( ) ) )
else :
self . _serial_pipe . close ( )
self . _serial_pipe = None
2015-09-13 18:40:09 +03:00
@asyncio.coroutine
def start_capture ( self , adapter_number , output_file ) :
"""
Starts a packet capture .
: param adapter_number : adapter number
: param output_file : PCAP destination file for the capture
"""
try :
adapter = self . _ethernet_adapters [ adapter_number ]
except KeyError :
raise VMwareError ( " Adapter {adapter_number} doesn ' t exist on VMware VM ' {name} ' " . format ( name = self . name ,
adapter_number = adapter_number ) )
nio = adapter . get_nio ( 0 )
if isinstance ( nio , NIOVMNET ) :
raise VMwareError ( " Sorry, packet capture is not supported without uBridge enabled " )
if not nio :
raise VMwareError ( " Adapter {} is not connected " . format ( adapter_number ) )
if nio . capturing :
raise VMwareError ( " Packet capture is already activated on adapter {adapter_number} " . format ( adapter_number = adapter_number ) )
nio . startPacketCapture ( output_file )
if self . _started :
yield from self . _start_ubridge_capture ( adapter_number , output_file )
log . info ( " VMware VM ' {name} ' [ {id} ]: starting packet capture on adapter {adapter_number} " . format ( name = self . name ,
id = self . id ,
adapter_number = adapter_number ) )
def stop_capture ( self , adapter_number ) :
"""
Stops a packet capture .
: param adapter_number : adapter number
"""
try :
adapter = self . _ethernet_adapters [ adapter_number ]
except KeyError :
raise VMwareError ( " Adapter {adapter_number} doesn ' t exist on VirtualBox VM ' {name} ' " . format ( name = self . name ,
adapter_number = adapter_number ) )
nio = adapter . get_nio ( 0 )
if not nio :
raise VMwareError ( " Adapter {} is not connected " . format ( adapter_number ) )
nio . stopPacketCapture ( )
if self . _started :
yield from self . _stop_ubridge_capture ( adapter_number )
log . info ( " VMware VM ' {name} ' [ {id} ]: stopping packet capture on adapter {adapter_number} " . format ( name = self . name ,
id = self . id ,
adapter_number = adapter_number ) )