OpenSecurity/bin/vmmanager.pyw
author BarthaM@N3SIM1218.D03.arc.local
Fri, 22 Aug 2014 09:35:02 +0100
changeset 218 327f282364b9
parent 217 4162648fb167
child 219 9480e5ba1a82
permissions -rwxr-xr-x
Removed checkResult method from cygwin.py.
Created additional tests and force removal of certificate image.
Modified the updateTemplate method.
Fixed VM startup wait method.
     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 # ------------------------------------------------------------
     5 # opensecurityd
     6 #   
     7 # the opensecurityd as RESTful server
     8 #
     9 # Autor: Mihai Bartha, <mihai.bartha@ait.ac.at>
    10 #
    11 # Copyright (C) 2013 AIT Austrian Institute of Technology
    12 # AIT Austrian Institute of Technology GmbH
    13 # Donau-City-Strasse 1 | 1220 Vienna | Austria
    14 # http://www.ait.ac.at
    15 #
    16 # This program is free software; you can redistribute it and/or
    17 # modify it under the terms of the GNU General Public License
    18 # as published by the Free Software Foundation version 2.
    19 # 
    20 # This program is distributed in the hope that it will be useful,
    21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23 # GNU General Public License for more details.
    24 # 
    25 # You should have received a copy of the GNU General Public License
    26 # along with this program; if not, write to the Free Software
    27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, 
    28 # Boston, MA  02110-1301, USA.
    29 # ------------------------------------------------------------
    30 
    31 
    32 # ------------------------------------------------------------
    33 # imports
    34 
    35 import os
    36 import os.path
    37 from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
    38 import sys
    39 import re
    40 
    41 from cygwin import Cygwin
    42 from environment import Environment
    43 import threading
    44 import time
    45 import string
    46 
    47 import shutil
    48 import stat
    49 import tempfile
    50 from opensecurity_util import logger, setupLogger, OpenSecurityException, showTrayMessage
    51 import ctypes
    52 import itertools
    53 import win32api
    54 import win32con
    55 import win32security
    56 import win32wnet
    57 import urllib
    58 import urllib2
    59 import unittest
    60 DEBUG = True
    61 
    62 
    63 new_sdvm_lock = threading.Lock()
    64 
    65 class VMManagerException(Exception):
    66     def __init__(self, value):
    67         self.value = value
    68     def __str__(self):
    69         return repr(self.value)
    70 
    71 class USBFilter:
    72     uuid = ""
    73     vendorid = ""
    74     productid = ""
    75     revision = ""
    76     serial = ""
    77     
    78     def __init__(self, uuid, vendorid, productid, revision, serial):
    79         self.uuid = uuid
    80         self.vendorid = vendorid.lower()
    81         self.productid = productid.lower()
    82         self.revision = revision.lower()
    83         self.serial = serial
    84         return
    85     
    86     def __eq__(self, other):
    87         return self.uuid == other.uuid #self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
    88     
    89     def __hash__(self):
    90         return hash(self.uuid) ^ hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision) ^ hash(self.serial)
    91     
    92     def __repr__(self):
    93         return "UUID:" + str(self.uuid) + " VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\'" + "\' Revision = \'" + str(self.revision) + "\' SerialNumber = \'" + str(self.serial)
    94     
    95     #def __getitem__(self, item):
    96     #    return self.coords[item]
    97 def once(theClass):
    98     theClass.systemProperties = theClass.getSystemProperties()
    99     theClass.machineFolder =    theClass.systemProperties["Default machine folder"]
   100     #theClass.hostonlyIF =       theClass.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"]
   101     theClass.blacklistedRSD =   theClass.loadRSDBlacklist()
   102     return theClass
   103     
   104 @once
   105 class VMManager(object):
   106     vmRootName = "SecurityDVM"
   107     systemProperties = None
   108     _instance = None
   109     machineFolder = ''
   110     rsdHandler = None
   111     hostonlyIF = None
   112     browsingManager = None
   113     blacklistedRSD = None
   114     status_message = 'Starting up...'
   115 
   116  
   117     def __init__(self):
   118         # only proceed if we have a working background environment
   119         if self.backend_ok():
   120             VMManager.hostonlyIF = self.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"]
   121             self.cleanup()
   122         else:
   123             logger.critical(self.status_message)
   124     
   125 
   126     @staticmethod
   127     def getInstance():
   128         if VMManager._instance == None:
   129             VMManager._instance = VMManager()
   130         return VMManager._instance
   131     
   132     #list the hostonly IFs exposed by the VBox host
   133     @staticmethod    
   134     def getHostOnlyIFs():
   135         results = Cygwin.vboxExecute('list hostonlyifs')[1]
   136         ifs = dict()
   137         if results=='':
   138             return ifs
   139         items = list( "Name:    " + result for result in results.split('Name:    ') if result != '')
   140         for item in items:
   141             if item != "":
   142                 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines()))
   143                 ifs[props["Name"]] = props
   144         return ifs
   145                     
   146         #props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   147         #return props
   148     
   149     #list the hostonly IFs exposed by the VBox host
   150     @staticmethod    
   151     def getDHCPServers():
   152         results = Cygwin.vboxExecute('list dhcpservers')[1]
   153         if results=='':
   154             return dict()
   155         items = list( "NetworkName:    " + result for result in results.split('NetworkName:    ') if result != '')
   156         dhcps = dict()   
   157         for item in items:
   158             if item != "":
   159                 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines()))
   160                 dhcps[props["NetworkName"]] = props
   161         return dhcps 
   162         
   163     # return hosty system properties
   164     @staticmethod
   165     def getSystemProperties():
   166         result = Cygwin.vboxExecute('list systemproperties')
   167         if result[1]=='':
   168             return None
   169         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
   170         return props
   171     
   172     # return the folder containing the guest VMs     
   173     def getMachineFolder(self):
   174         return VMManager.machineFolder
   175     
   176     # verifies the hostonly interface and DHCP server settings
   177     def verifyHostOnlySettings(self):
   178         interfaceName = "VirtualBox Host-Only Ethernet Adapter"
   179         networkName = "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter"
   180         
   181         hostonlyifs = self.getHostOnlyIFs()
   182         if not interfaceName in hostonlyifs.keys():
   183             Cygwin.vboxExecute('hostonlyif create')
   184             hostonlyifs = self.getHostOnlyIFs()
   185             if not interfaceName in hostonlyifs.keys():
   186                 return False
   187         
   188         interface = hostonlyifs[interfaceName]
   189         if interface['VBoxNetworkName'] != networkName or interface['DHCP'] != 'Disabled' or interface['IPAddress'] != '192.168.56.1':
   190             Cygwin.vboxExecute('hostonlyif ipconfig "' + interfaceName + '" --ip 192.168.56.1 --netmask 255.255.255.0')
   191             
   192         dhcpservers = self.getDHCPServers()
   193         if not networkName in dhcpservers.keys():
   194             Cygwin.vboxExecute('dhcpserver add --ifname "' + interfaceName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable')
   195             dhcpservers = self.getDHCPServers()
   196             if not networkName in dhcpservers.keys():
   197                 return False
   198             
   199         server = dhcpservers[networkName]
   200         if server['IP'] != '192.168.56.100' or server['NetworkMask'] != '255.255.255.0' or server['lowerIPAddress'] != '192.168.56.101' or server['upperIPAddress'] != '192.168.56.254' or server['Enabled'] != 'Yes':
   201             Cygwin.vboxExecute('VBoxManage dhcpserver modify --netname "' + networkName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable')
   202         
   203         return True
   204 
   205     def backend_ok(self):
   206 
   207         """check if the backend (VirtualBox) is sufficient for our task"""
   208 
   209         # ensure we have our system props
   210         if VMManager.systemProperties == None:
   211             VMManager.systemProperties = self.getSystemProperties()
   212         if VMManager.systemProperties == None:
   213             self.status_message = 'Failed to get backend system properties. Is Backend (VirtualBox?) installed?'
   214             return False
   215 
   216         # check for existing Extension pack
   217         if not 'Remote desktop ExtPack' in VMManager.systemProperties:
   218             self.status_message = 'No remote desktop extension pack found. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.'
   219             return False
   220         if VMManager.systemProperties['Remote desktop ExtPack'] == 'Oracle VM VirtualBox Extension Pack ':
   221             self.status_message = 'Unsure if suitable extension pack is installed. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.'
   222             return False
   223 
   224         # check if we do have our root VMs installed
   225         vms = self.listVM()
   226         if not self.vmRootName in vms:
   227             self.status_message = 'Unable to locate root SecurityDVM. Please download and setup the initial image.'
   228             return False
   229 
   230         # basically all seems nice and ready to rumble
   231         self.status_message = 'All is ok.'
   232 
   233         self.verifyHostOnlySettings()
   234         
   235         return True
   236     
   237     def stop(self):
   238         if self.rsdHandler != None:
   239             self.rsdHandler.stop()
   240             self.rsdHandler.join()
   241             self.rsdHandler = None
   242             
   243         if self.browsingManager != None:
   244             self.browsingManager.stop()
   245             self.browsingManager.join()
   246             self.browsingManager = None
   247     
   248     def start(self):
   249         self.stop()
   250         self.browsingManager = BrowsingManager(self)
   251         self.browsingManager.start()
   252         self.rsdHandler = DeviceHandler(self)
   253         self.rsdHandler.start()
   254         
   255 
   256     def cleanup(self):
   257         self.stop()
   258         ip = self.getHostOnlyIP(None)
   259         try:
   260             result = urllib2.urlopen('http://127.0.0.1:8090/netcleanup?'+'hostonly_ip='+ip).readline()
   261         except urllib2.URLError:
   262             logger.info("Network drive cleanup all skipped. OpenSecurity Tray client not started yet.")
   263             
   264         for vm in self.listSDVM():
   265             self.poweroffVM(vm)
   266             self.removeVM(vm)
   267 
   268     # list all existing VMs registered with VBox
   269     def listVM(self):
   270         result = Cygwin.vboxExecute('list vms')[1]
   271         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   272         return vms
   273     
   274     # list running VMs
   275     def listRunningVMS(self):
   276         result = Cygwin.vboxExecute('list runningvms')[1]
   277         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   278         return vms
   279     
   280     # list existing SDVMs
   281     def listSDVM(self):
   282         vms = self.listVM()
   283         svdms = []
   284         for vm in vms:
   285             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   286                 svdms.append(vm)
   287         return svdms
   288     
   289     # generate valid (not already existing SDVM name). necessary for creating a new VM
   290     def genSDVMName(self):
   291         vms = self.listVM()
   292         for i in range(0,999):
   293             if(not self.vmRootName+str(i) in vms):
   294                 return self.vmRootName+str(i)
   295         return ''
   296     
   297     @staticmethod
   298     def loadRSDBlacklist():
   299         blacklist = dict()
   300         try:
   301             fo = open(Environment('OpenSecurity').prefix_path +"\\bin\\blacklist.usb", "r")
   302         except IOError:
   303             logger.error("Could not open RSD blacklist file.")
   304             return blacklist
   305         
   306         lines = fo.readlines()
   307         for line in lines:
   308             if line != "":  
   309                 parts = line.strip().split(' ')
   310                 blacklist[parts[0].lower()] = parts[1].lower()
   311         return blacklist
   312          
   313     @staticmethod
   314     def isBlacklisted(device):
   315         if VMManager.blacklistedRSD:
   316             blacklisted = device.vendorid.lower() in VMManager.blacklistedRSD.keys() and device.productid.lower() == VMManager.blacklistedRSD[device.vendorid]
   317             return blacklisted
   318         return False 
   319     
   320     # check if the device is mass storage type
   321     @staticmethod
   322     def isMassStorageDevice(device):
   323         keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   324         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname)
   325         devinfokeyname = win32api.RegEnumKey(key, 0)
   326         win32api.RegCloseKey(key)
   327 
   328         devinfokey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   329         value = win32api.RegQueryValueEx(devinfokey, 'SERVICE')[0]
   330         win32api.RegCloseKey(devinfokey)
   331         
   332         return 'USBSTOR' in value
   333     
   334     # return the RSDs connected to the host
   335     @staticmethod
   336     def getExistingRSDs():
   337         results = Cygwin.vboxExecute('list usbhost')[1]
   338         results = results.split('Host USB Devices:')[1].strip()
   339         
   340         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   341         rsds = dict()   
   342         for item in items:
   343             props = dict()
   344             for line in item.splitlines():     
   345                 if line != "":         
   346                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   347                     props[k] = v
   348             
   349             uuid = re.search(r"(?P<uuid>[0-9A-Fa-f\-]+)", props['UUID']).groupdict()['uuid']
   350             vid = re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid']
   351             pid = re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid']
   352             rev = re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev']
   353             serial = None
   354             if 'SerialNumber' in props.keys():
   355                 serial = re.search(r"(?P<ser>[0-9A-Fa-f]+)", props['SerialNumber']).groupdict()['ser']
   356             usb_filter = USBFilter( uuid, vid, pid, rev, serial)
   357              
   358             if VMManager.isMassStorageDevice(usb_filter) and not VMManager.isBlacklisted(usb_filter):
   359                 rsds[uuid] = usb_filter
   360                 logger.debug(usb_filter)
   361         return rsds
   362     
   363    
   364     #def getAttachedRSD(self, vm_name):
   365     #    props = self.getVMInfo(vm_name)
   366     #    keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1', 'USBFilterSerialNumber1'])
   367     #    keyset = set(props.keys())
   368     #    usb_filter = None
   369     #    if keyset.issuperset(keys):
   370     #        usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
   371     #    return usb_filter
   372     
   373     # return the attached USB device as usb descriptor for an existing VM 
   374     def getAttachedRSD(self, vm_name):
   375         props = self.getVMInfo(vm_name)
   376         keys = set(['USBAttachedUUID1', 'USBAttachedVendorId1', 'USBAttachedProductId1', 'USBAttachedRevision1', 'USBAttachedSerialNumber1'])
   377         keyset = set(props.keys())
   378         usb_filter = None
   379         if keyset.issuperset(keys):
   380             usb_filter = USBFilter(props['USBAttachedUUID1'], props['USBAttachedVendorId1'], props['USBAttachedProductId1'], props['USBAttachedRevision1'], props['USBAttachedSerialNumber1'])
   381         return usb_filter
   382         
   383     # return the RSDs attached to all existing SDVMs
   384     def getAttachedRSDs(self):
   385         vms = self.listSDVM()
   386         attached_devices = dict()
   387         for vm in vms:
   388             rsd_filter = self.getAttachedRSD(vm)
   389             if rsd_filter != None:
   390                 attached_devices[vm] = rsd_filter
   391         return attached_devices
   392     
   393     # attach removable storage device to VM by provision of filter
   394     def attachRSD(self, vm_name, rsd_filter):
   395         #return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision + ' --serialnumber ' + rsd_filter.serial)
   396         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbattach ' + rsd_filter.uuid )
   397     
   398     # detach removable storage from VM by 
   399     def detachRSD(self, vm_name, rsd_filter):
   400         #return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name)
   401         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbdetach ' + rsd_filter.uuid )
   402         
   403     # configures hostonly networking and DHCP server. requires admin rights
   404     def configureHostNetworking(self):
   405         #cmd = 'vboxmanage list hostonlyifs'
   406         #Cygwin.vboxExecute(cmd)
   407         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   408         #Cygwin.vboxExecute(cmd)
   409         #cmd = 'vboxmanage hostonlyif create'
   410         #Cygwin.vboxExecute(cmd)
   411         Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
   412         #cmd = 'vboxmanage dhcpserver add'
   413         #Cygwin.vboxExecute(cmd)
   414         Cygwin.vboxExecute('dhcpserver modify --ifname \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.200')
   415     
   416     def isSDVMExisting(self, vm_name):
   417         sdvms = self.listSDVM()
   418         return vm_name in sdvms
   419         
   420     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   421     def createVM(self, vm_name):
   422         if self.isSDVMExisting(vm_name):
   423             return
   424         #remove eventually existing SDVM folder
   425         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   426         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"')
   427         Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
   428         Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 768 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + self.hostonlyIF['Name'] + '\" --nic2 nat')
   429         Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
   430 
   431     #create new SecurityDVM with automatically generated name from template (thread safe)        
   432     def newSDVM(self):
   433         with new_sdvm_lock:
   434             vm_name = self.genSDVMName()
   435             self.createVM(vm_name)
   436         return vm_name
   437     
   438     # attach storage image to controller
   439     def attachStorage(self, vm_name):
   440         if self.isStorageAttached(vm_name):
   441             self.detachStorage(vm_name)
   442         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ VMManager.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"')
   443     
   444     # return true if storage is attached 
   445     def isStorageAttached(self, vm_name):
   446         info = self.getVMInfo(vm_name)
   447         return (info['SATA-0-0']!='none')
   448     
   449     # detach storage from controller
   450     def detachStorage(self, vm_name):
   451         if self.isStorageAttached(vm_name):
   452             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none')
   453     
   454     def changeStorageType(self, filename, storage_type):
   455         Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type)
   456                 
   457     # list storage snaphots for VM
   458     def updateTemplate(self):
   459         self.stop()
   460         self.cleanup()
   461         self.poweroffVM(self.vmRootName)
   462         self.waitShutdown(self.vmRootName)
   463         
   464         # check for updates
   465         self.genCertificate(self.vmRootName)
   466         self.attachCertificate(self.vmRootName)
   467         
   468         #templateUUID = self.getVMInfo(self.vmRootName)["SATA-ImageUUID-0-0"] #TODO: // verify value
   469         templateUUID = self.getTemplateUUID()
   470         
   471         self.detachStorage(self.vmRootName)
   472         self.removeSnapshots(templateUUID)
   473         
   474         template_storage = VMManager.machineFolder + '\\' + self.vmRootName + '\\' + self.vmRootName + '.vmdk'
   475         #TODO:// modify to take vm name as argument
   476         self.changeStorageType(template_storage,'normal')
   477         self.attachStorage(self.vmRootName)
   478         self.startVM(self.vmRootName)
   479         self.waitStartup(self.vmRootName)
   480         
   481         tmp_ip = self.getHostOnlyIP(self.vmRootName)
   482         tmp_machine_folder = Cygwin.cygPath(VMManager.machineFolder)
   483         Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   484         Cygwin.sshExecute('"sudo apt-get -y upgrade"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   485         
   486         #check if reboot is required
   487         result = Cygwin.sshExecute('"if [ -f /var/run/reboot-required ]; then echo \\\"Yes\\\"; fi"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   488         if "Yes" in result[1]:
   489             self.stopVM(self.vmRootName)
   490             self.waitShutdown(self.vmRootName)
   491             self.startVM(self.vmRootName)
   492             self.waitStartup(self.vmRootName)
   493         
   494         #self.hibernateVM(self.vmRootName)
   495         self.stopVM(self.vmRootName)
   496         self.waitShutdown(self.vmRootName)
   497         self.detachStorage(self.vmRootName)
   498         self.changeStorageType(template_storage,'immutable')
   499         self.attachStorage(self.vmRootName)
   500         
   501         #self.start()
   502 
   503     #"SATA-0-0"="C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\Snapshots\{d0af827d-f13a-49be-8ac1-df20b13bda83}.vmdk"
   504     #"SATA-ImageUUID-0-0"="d0af827d-f13a-49be-8ac1-df20b13bda83"
   505     @staticmethod    
   506     def getDiskImages():
   507         results = Cygwin.vboxExecute('list hdds')[1]
   508         results = results.replace('Parent UUID', 'Parent')
   509         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   510         
   511         snaps = dict()   
   512         for item in items:
   513             props = dict()
   514             for line in item.splitlines():
   515                 if line != "":         
   516                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   517                     props[k] = v;
   518             snaps[props['UUID']] = props
   519         return snaps
   520     
   521     @staticmethod 
   522     def getTemplateUUID():
   523         images = VMManager.getDiskImages()
   524         template_storage = VMManager.machineFolder + '\\' + VMManager.vmRootName + '\\' + VMManager.vmRootName + '.vmdk'
   525         # find template uuid
   526         template_uuid = None
   527         for hdd in images.values():
   528             if hdd['Location'] == template_storage:
   529                 template_uuid = hdd['UUID']
   530                 break
   531         return template_uuid
   532         
   533     def removeSnapshots(self, imageUUID):
   534         snaps = self.getDiskImages()
   535         # remove snapshots 
   536         for hdd in snaps.values():
   537             if hdd['Parent'] == imageUUID:
   538                 snapshotUUID = hdd['UUID']
   539                 self.removeImage(snapshotUUID)
   540                 
   541     def removeImage(self, imageUUID):
   542         logger.debug('removing snapshot ' + imageUUID)
   543         Cygwin.vboxExecute('closemedium disk {' + imageUUID + '} --delete')
   544         # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   545     
   546     #remove VM from the system. should be used on VMs returned by listSDVMs    
   547     def removeVM(self, vm_name):
   548         logger.info('Removing ' + vm_name)
   549         
   550         Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
   551         #TODO:// try to close medium if still existing
   552         #Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')
   553         self.removeVMFolder(vm_name)
   554     
   555     def removeVMFolder(self, vm_name):
   556         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   557         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"')
   558     
   559     # start VM
   560     def startVM(self, vm_name):
   561         logger.info('Starting ' +  vm_name)
   562         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   563         result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
   564         while 'successfully started' not in result[1]:
   565             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   566             logger.error("Command returned:\n" + result[2])
   567             time.sleep(1)
   568             result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
   569         return result[0]
   570     
   571     # return wether VM is running or not
   572     def isVMRunning(self, vm_name):
   573         return vm_name in self.listRunningVMS()    
   574     
   575     # stop VM
   576     def stopVM(self, vm_name):
   577         logger.info('Sending shutdown signal to ' + vm_name)
   578         Cygwin.sshExecute( '"sudo shutdown -h now"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key' )
   579         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   580     
   581     # stop VM
   582     def hibernateVM(self, vm_name):
   583         logger.info('Sending hibernate-disk signal to ' + vm_name)
   584         Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False)
   585         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   586             
   587     # poweroff VM
   588     def poweroffVM(self, vm_name):
   589         if not self.isVMRunning(vm_name):
   590             return
   591         logger.info('Powering off ' + vm_name)
   592         Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   593         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   594     
   595     
   596     # return the hostOnly IP for a running guest or the host    
   597     def getHostOnlyIP(self, vm_name):
   598         if vm_name == None:
   599             logger.info('Getting hostOnly IP address for Host')
   600             return VMManager.hostonlyIF['IPAddress']
   601         else:
   602             logger.info('Getting hostOnly IP address ' + vm_name)
   603             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   604             if result=='':
   605                 return None
   606             result = result[1]
   607             if result.startswith('No value set!'):
   608                 return None
   609             return result[result.index(':')+1:].strip()
   610         
   611     # return the description set for an existing VM
   612     def getVMInfo(self, vm_name):
   613         results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
   614         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   615         return props
   616     
   617     #generates ISO containing authorized_keys for use with guest VM
   618     def genCertificate(self, vm_name):
   619         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   620         # remove .ssh folder if exists
   621         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   622         # remove .ssh folder if exists
   623         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   624         # create .ssh folder in vm_name
   625         Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   626         # generate dvm_key pair in vm_name / .ssh     
   627         Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"')
   628         # move out private key
   629         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
   630         # set permissions for private key
   631         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   632         # rename public key to authorized_keys
   633         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   634         # set permissions for authorized_keys
   635         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   636         # generate iso image with .ssh/authorized keys
   637         Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   638     
   639     # attaches generated ssh public cert to guest vm
   640     def attachCertificate(self, vm_name):
   641         if self.isCertificateAttached(vm_name):
   642             self.detachCertificate(vm_name)
   643         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + VMManager.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   644     
   645     # return true if storage is attached 
   646     def isCertificateAttached(self, vm_name):
   647         info = self.getVMInfo(vm_name)
   648         return (info['SATA-1-0']!='none')
   649     
   650     # detach storage from controller
   651     def detachCertificate(self, vm_name):
   652         if self.isCertificateAttached(vm_name):
   653             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type hdd --medium none')
   654             
   655     # wait for machine to come up
   656     def waitStartup(self, vm_name):
   657         #Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout', try_count = 60)
   658         started = False
   659         while not started:
   660             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' SDVMStarted')[1]
   661             if "Value: True" in result:
   662                 started = True
   663             else:
   664                 time.sleep(3) 
   665         return self.getHostOnlyIP(vm_name)
   666     
   667     # wait for machine to shutdown
   668     def waitShutdown(self, vm_name):
   669         while vm_name in self.listRunningVMS():
   670             time.sleep(1)
   671         return
   672     
   673     #Small function to check if the mentioned location is a directory
   674     def isDirectory(self, path):
   675         result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
   676         return string.find(result[1], 'DIR',)
   677     
   678     def genNetworkDrive(self):
   679         logical_drives = VMManager.getLogicalDrives()
   680         logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') )
   681         drives = list(map(chr, range(68, 91)))  
   682         for drive in drives:
   683             if drive not in logical_drives:
   684                 return drive
   685             
   686     @staticmethod
   687     def getLogicalDrives():
   688         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   689         drives = list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   690         return drives
   691     
   692     @staticmethod
   693     def getDriveType(drive):
   694         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   695     
   696     @staticmethod
   697     def getNetworkPath(drive):
   698         return win32wnet.WNetGetConnection(drive+':')
   699     
   700     @staticmethod
   701     def getVolumeInfo(drive):
   702         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   703         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   704         serial_number = None
   705         max_component_length = None
   706         file_system_flags = None
   707         
   708         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   709             u"%s:\\"%drive,
   710             volumeNameBuffer,
   711             ctypes.sizeof(volumeNameBuffer),
   712             serial_number,
   713             max_component_length,
   714             file_system_flags,
   715             fileSystemNameBuffer,
   716             ctypes.sizeof(fileSystemNameBuffer)
   717         )
   718         return volumeNameBuffer.value, fileSystemNameBuffer.value
   719     
   720     def getNetworkDrive(self, vm_name):
   721         ip = self.getHostOnlyIP(vm_name)
   722         if ip == None:
   723             logger.error("Failed getting hostonly IP for " + vm_name)
   724             return None
   725         logger.info("Got IP address for " + vm_name + ': ' + ip)
   726         for drive in VMManager.getLogicalDrives():
   727             #if is a network drive
   728             if VMManager.getDriveType(drive) == 4:
   729                 network_path = VMManager.getNetworkPath(drive)
   730                 if ip in network_path:
   731                     return drive
   732         return None
   733     
   734     def getNetworkDrives(self):
   735         ip = self.getHostOnlyIP(None)
   736         if ip == None:
   737             logger.error("Failed getting hostonly IP for system")
   738             return None
   739         logger.info("Got IP address for system: " + ip)
   740         ip = ip[:ip.rindex('.')]
   741         network_drives = dict()
   742         for drive in VMManager.getLogicalDrives():
   743             #if is a network drive
   744             if VMManager.getDriveType(drive) == 4:
   745                 network_path = VMManager.getNetworkPath(drive)
   746                 if ip in network_path:
   747                     network_drives[drive] = network_path  
   748         return network_drives
   749     
   750     # handles browsing request    
   751     def handleBrowsingRequest(self, proxy):
   752         showTrayMessage('Starting Secure Browsing...', 7000)
   753         handler = BrowsingHandler(self, proxy)
   754         handler.start()
   755         return 'ok'
   756     
   757     def getActiveUserName(self):
   758         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI')
   759         v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0])
   760         win32api.RegCloseKey(key)
   761         user_name = win32api.ExpandEnvironmentStrings(v)
   762         return user_name
   763         
   764     def getUserSID(self, user_name):
   765         domain, user = user_name.split("\\")
   766         account_name = win32security.LookupAccountName(domain, user)
   767         if account_name == None:
   768             logger.error("Failed lookup account name for user " + user_name)
   769             return None
   770         sid = win32security.ConvertSidToStringSid(account_name[0])
   771         if sid == None:
   772             logger.error("Failed converting SID for account " + account_name[0])
   773             return None
   774         return sid
   775         
   776     def getAppDataDir(self, sid):    
   777         key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
   778         value, type = win32api.RegQueryValueEx(key, "AppData")
   779         win32api.RegCloseKey(key)
   780         return value
   781         
   782         #key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + '\\' + sid)
   783         #value, type = win32api.RegQueryValueEx(key, "ProfileImagePath")
   784         #print value
   785     
   786     def backupFile(self, src, dest):
   787         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   788         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + self.browsingManager.ip_addr + ':' + src + '" "' + dest + '"'
   789         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   790     
   791     def restoreFile(self, src, dest):
   792         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   793         #command = '-r -v -o StrictHostKeyChecking=no -i \"' + certificate + '\" \"' + src + '\" \"osecuser@' + self.browsingManager.ip_addr + ':' + dest + '\"'
   794         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + self.browsingManager.ip_addr + ':' + dest + '"'
   795         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)    
   796 
   797 #handles browsing session creation 
   798 class BrowsingHandler(threading.Thread):
   799     vmm = None
   800     proxy = None
   801     def __init__(self, vmmanager, proxy):
   802         threading.Thread.__init__(self)
   803         self.vmm = vmmanager
   804         self.proxy = proxy
   805         
   806     def run(self):
   807         #browser = '\\\"/usr/bin/chromium; pidof dbus-launch | xargs kill\\\"'
   808         #browser = '\\\"/usr/bin/chromium\\\"'
   809         
   810         try:
   811             if self.proxy:
   812                 browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"'
   813             else:
   814                 browser = '\\\"/usr/bin/chromium\\\"'
   815             self.vmm.browsingManager.started.wait() 
   816             result = Cygwin.sshExecuteX11(browser, self.vmm.browsingManager.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vmm.browsingManager.vm_name + '/dvm_key')
   817             self.vmm.backupFile('/home/osecuser/.config/chromium', self.vmm.browsingManager.appDataDir + '/OpenSecurity/')
   818         except:
   819             logger.info("BrowsingHandler closing. Restarting browsing SDVM.")
   820 
   821         self.vmm.browsingManager.restart.set()
   822         
   823             
   824 # handles browsing Vm creation and destruction                    
   825 class BrowsingManager(threading.Thread):   
   826     vmm = None
   827     running = True
   828     restart = None
   829     ip_addr = None
   830     vm_name = None
   831     net_resource = None
   832     appDataDir = None
   833     
   834     def __init__(self, vmmanager):
   835         threading.Thread.__init__(self)
   836         self.vmm = vmmanager
   837         self.restart = threading.Event()
   838         self.started = threading.Event()
   839     
   840     def stop(self):
   841         self.running = False
   842         self.restart.set()   
   843      
   844     def run(self):
   845         while self.running:
   846             self.restart.clear()
   847             self.started.clear()
   848             
   849             if self.net_resource == None:
   850                 logger.info("Missing browsing SDVM's network share. Skipping disconnect")
   851             else:
   852                 try:
   853                     browsing_vm = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline()
   854                     self.net_resource = None
   855                 except urllib2.URLError:
   856                     logger.error("Network share disconnect failed. OpenSecurity Tray client not running.")
   857                     continue
   858             
   859             self.ip_addr = None
   860 
   861             if self.vm_name != None:
   862                 self.vmm.poweroffVM(self.vm_name)
   863                 self.vmm.removeVM(self.vm_name)
   864             
   865             try:
   866                 self.vm_name = self.vmm.newSDVM()
   867                 self.vmm.attachStorage(self.vm_name)
   868                 self.vmm.genCertificate(self.vm_name)
   869                 self.vmm.attachCertificate(self.vm_name)
   870                 
   871                 self.vmm.startVM(self.vm_name)
   872                 
   873                 self.ip_addr = self.vmm.waitStartup(self.vm_name)
   874                 if self.ip_addr == None:
   875                     logger.error("Failed to get ip address")
   876                     continue
   877                 else:
   878                     logger.info("Got IP address for " + self.vm_name + ' ' + self.ip_addr)
   879                 
   880                 try:
   881                     self.net_resource = '\\\\' + self.ip_addr + '\\Download'
   882                     result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource).readline()
   883                 except urllib2.URLError:
   884                     logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   885                     self.net_resource = None
   886                     continue
   887                 
   888                 user = self.vmm.getActiveUserName()
   889                 if user == None:
   890                     logger.error("Cannot get active user name")
   891                     continue
   892                 else:
   893                     logger.info('Got active user name ' + user)
   894                 sid = self.vmm.getUserSID(user)
   895                 if sid == None:
   896                     logger.error("Cannot get SID for active user")
   897                     continue
   898                 else:
   899                     logger.info("Got active user SID " + sid + " for user " + user)
   900                     
   901                 path = self.vmm.getAppDataDir(sid)
   902                 if path == None:
   903                     logger.error("Cannot get AppDataDir for active user")
   904                     continue
   905                 else:
   906                     logger.info("Got AppData dir for user " + user + ': ' + path)
   907                 
   908                 self.appDataDir = Cygwin.cygPath(path)
   909                 logger.info("Restoring browser settings in AppData dir " + self.appDataDir)
   910                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
   911                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity\\\"')
   912                 # create chromium settings dir on local machine if not existing
   913                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity/chromium\\\"')
   914                 # create chromium settings dir on remote machine if not existing
   915                 Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key')
   916                 #restore settings on vm
   917                 self.vmm.restoreFile(self.appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
   918                 self.started.set()
   919                 logger.info("Browsing SDVM running.")
   920                 self.restart.wait()
   921             except OpenSecurityException, e:
   922                 logger.error(''.join(e))
   923             except:
   924                 logger.error("Unexpected error: " + sys.exc_info()[0])
   925                 logger.error("BrowsingHandler failed. Cleaning up")
   926                 #self.running= False
   927                 
   928 class DeviceHandler(threading.Thread): 
   929     vmm = None
   930     existingRSDs = None
   931     attachedRSDs = None  
   932     running = True
   933     def __init__(self, vmmanger): 
   934         threading.Thread.__init__(self)
   935         self.vmm = vmmanger
   936  
   937     def stop(self):
   938         self.running = False
   939         
   940     def run(self):
   941         
   942         self.existingRSDs = dict()
   943         self.attachedRSDs = self.vmm.getAttachedRSDs()
   944         
   945         while self.running:
   946             tmp_rsds = self.vmm.getExistingRSDs()
   947             if tmp_rsds.keys() == self.existingRSDs.keys():
   948                 logger.debug("Nothing's changed. sleep(3)")
   949                 time.sleep(3)
   950                 continue
   951             
   952             showTrayMessage('System changed.\nEvaluating...', 7000)
   953             logger.info("Something's changed")
   954             tmp_attached = self.attachedRSDs     
   955             for vm_name in tmp_attached.keys():
   956                 if tmp_attached[vm_name] not in tmp_rsds.values():
   957                     ip = self.vmm.getHostOnlyIP(vm_name)
   958                     if ip == None:
   959                         logger.error("Failed getting hostonly IP for " + vm_name)
   960                         continue
   961                     try:
   962                         net_resource = '\\\\' + ip + '\\USB'
   963                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline()
   964                     except urllib2.URLError:
   965                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   966                         continue
   967                     
   968                     # detach not necessary as already removed from vm description upon disconnect
   969                     #self.vmm.detachRSD(vm_name, self.attachedRSDs[vm_name])
   970                     del self.attachedRSDs[vm_name]
   971                     self.vmm.poweroffVM(vm_name)
   972                     self.vmm.removeVM(vm_name)
   973                     #break
   974                     
   975             #create new vms for new devices if any
   976             new_ip = None
   977             for new_device in tmp_rsds.values():
   978                 showTrayMessage('Mounting device...', 7000)
   979                 if (self.attachedRSDs and False) or (new_device not in self.attachedRSDs.values()):
   980                     new_sdvm = self.vmm.newSDVM()
   981                     self.vmm.attachStorage(new_sdvm)
   982                     self.vmm.startVM(new_sdvm)
   983                     new_ip = self.vmm.waitStartup(new_sdvm)
   984                     if new_ip == None:
   985                         logger.error("Error getting IP address of SDVM. Cleaning up.")
   986                         self.vmm.poweroffVM(new_sdvm)
   987                         self.vmm.removeVM(new_sdvm)
   988                         continue
   989                     else:
   990                         logger.info("Got IP address for " + new_sdvm + ' ' + new_ip)
   991                     try:
   992                         self.vmm.attachRSD(new_sdvm, new_device)
   993                         self.attachedRSDs[new_sdvm] = new_device
   994                     except:
   995                         logger.info("RSD prematurely removed. Cleaning up.")
   996                         self.vmm.poweroffVM(new_sdvm)
   997                         self.vmm.removeVM(new_sdvm)
   998                         continue
   999                     try:
  1000                         net_resource = '\\\\' + new_ip + '\\USB'
  1001                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+net_resource).readline()
  1002                     except urllib2.URLError:
  1003                         logger.error("Network drive connect failed (tray client not accessible). Cleaning up.")
  1004                         self.vmm.poweroffVM(new_sdvm)
  1005                         self.vmm.removeVM(new_sdvm)
  1006                         continue
  1007                     
  1008             self.existingRSDs = tmp_rsds