OpenSecurity/bin/vmmanager.pyw
author BarthaM@N3SIM1218.D03.arc.local
Thu, 14 Aug 2014 09:51:11 +0100
changeset 217 4162648fb167
parent 213 2e0b94e12bfc
child 218 327f282364b9
permissions -rwxr-xr-x
Fixed black tray icon issue (new method for checking if SDVM is started)
New configuration check on the hostonly IF and DHCP server
     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.checkResult(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.checkResult(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.checkResult(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.checkResult(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.checkResult(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.checkResult(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.checkResult(Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name))
   401         return Cygwin.checkResult(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.checkResult(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.checkResult(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.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   427         Cygwin.checkResult(Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register'))
   428         Cygwin.checkResult(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.checkResult(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 storageAttach(self, vm_name):
   440         if self.isStorageAttached(vm_name):
   441             self.storageDetach(vm_name)
   442         Cygwin.checkResult(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 storageDetach(self, vm_name):
   451         if self.isStorageAttached(vm_name):
   452             Cygwin.checkResult(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.checkResult(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.genCertificateISO(self.vmRootName)
   466         self.attachCertificateISO(self.vmRootName)
   467         
   468         #templateUUID = self.getVMInfo(self.vmRootName)["SATA-ImageUUID-0-0"] #TODO: // verify value
   469         templateUUID = self.getTemplateUUID()
   470         
   471         self.storageDetach(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.storageAttach(self.vmRootName)
   478         self.startVM(self.vmRootName)
   479         self.waitStartup(self.vmRootName, timeout_ms = 30000)
   480         
   481         tmp_ip = self.getHostOnlyIP(self.vmRootName)
   482         tmp_machine_folder = Cygwin.cygPath(VMManager.machineFolder)
   483         Cygwin.checkResult(Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key'))
   484         Cygwin.checkResult(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.checkResult(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.storageDetach(self.vmRootName)
   498         self.changeStorageType(template_storage,'immutable')
   499         self.storageAttach(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.checkResult(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.checkResult(Cygwin.vboxExecute('closemedium disk {' + imageUUID + '} --delete'))#[1]
   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.checkResult(Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete'))
   551         #TODO:// try to close medium if still existing
   552         #Cygwin.checkResult(Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete'))#[1]
   553         self.removeVMFolder(vm_name)
   554     
   555     def removeVMFolder(self, vm_name):
   556         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   557         Cygwin.checkResult(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         #TODO: modify to use Cygwin.checkResult() of make it retry 3 times
   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.checkResult(Cygwin.sshExecute( '"sudo shutdown -h now"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key' ))
   579     
   580     # stop VM
   581     def hibernateVM(self, vm_name):
   582         logger.info('Sending hibernate-disk signal to ' + vm_name)
   583         Cygwin.checkResult(Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False))
   584             
   585     # poweroff VM
   586     def poweroffVM(self, vm_name):
   587         if not self.isVMRunning(vm_name):
   588             return
   589         logger.info('Powering off ' + vm_name)
   590         return Cygwin.checkResult(Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff'))
   591     
   592     # return the hostOnly IP for a running guest or the host    
   593     def getHostOnlyIP(self, vm_name):
   594         if vm_name == None:
   595             logger.info('Getting hostOnly IP address for Host')
   596             return VMManager.hostonlyIF['IPAddress']
   597         else:
   598             logger.info('Getting hostOnly IP address ' + vm_name)
   599             result = Cygwin.checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'))
   600             if result=='':
   601                 return None
   602             result = result[1]
   603             if result.startswith('No value set!'):
   604                 return None
   605             return result[result.index(':')+1:].strip()
   606         
   607     # return the description set for an existing VM
   608     def getVMInfo(self, vm_name):
   609         results = Cygwin.checkResult(Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable'))[1]
   610         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   611         return props
   612     
   613     #generates ISO containing authorized_keys for use with guest VM
   614     def genCertificateISO(self, vm_name):
   615         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   616         # remove .ssh folder if exists
   617         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   618         # remove .ssh folder if exists
   619         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"'))
   620         # create .ssh folder in vm_name
   621         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   622         # generate dvm_key pair in vm_name / .ssh     
   623         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"'))
   624         # move out private key
   625         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   626         # set permissions for private key
   627         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"'))
   628         # rename public key to authorized_keys
   629         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
   630         # set permissions for authorized_keys
   631         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
   632         # generate iso image with .ssh/authorized keys
   633         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   634     
   635     # attaches generated ssh public cert to guest vm
   636     def attachCertificateISO(self, vm_name):
   637         result = Cygwin.checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + VMManager.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"'))
   638         return result
   639     
   640     # wait for machine to come up
   641     def waitStartup(self, vm_name, timeout_ms = 1000):
   642         #Cygwin.checkResult(Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout', try_count = 60))
   643         started = False
   644         while not started:
   645             result = Cygwin.checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' SDVMStarted'))[1]
   646             if "Value: True" in result:
   647                 started = True
   648             else:
   649                 time.sleep(3) 
   650         return self.getHostOnlyIP(vm_name)
   651     
   652     # wait for machine to shutdown
   653     def waitShutdown(self, vm_name):
   654         while vm_name in self.listRunningVMS():
   655             time.sleep(1)
   656         return
   657     
   658     #Small function to check if the mentioned location is a directory
   659     def isDirectory(self, path):
   660         result = Cygwin.checkResult(Cygwin.cmdExecute('dir ' + path + ' | FIND ".."'))
   661         return string.find(result[1], 'DIR',)
   662     
   663     def genNetworkDrive(self):
   664         logical_drives = VMManager.getLogicalDrives()
   665         logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') )
   666         drives = list(map(chr, range(68, 91)))  
   667         for drive in drives:
   668             if drive not in logical_drives:
   669                 return drive
   670             
   671     @staticmethod
   672     def getLogicalDrives():
   673         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   674         drives = list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   675         return drives
   676     
   677     @staticmethod
   678     def getDriveType(drive):
   679         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   680     
   681     @staticmethod
   682     def getNetworkPath(drive):
   683         return win32wnet.WNetGetConnection(drive+':')
   684     
   685     @staticmethod
   686     def getVolumeInfo(drive):
   687         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   688         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   689         serial_number = None
   690         max_component_length = None
   691         file_system_flags = None
   692         
   693         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   694             u"%s:\\"%drive,
   695             volumeNameBuffer,
   696             ctypes.sizeof(volumeNameBuffer),
   697             serial_number,
   698             max_component_length,
   699             file_system_flags,
   700             fileSystemNameBuffer,
   701             ctypes.sizeof(fileSystemNameBuffer)
   702         )
   703         return volumeNameBuffer.value, fileSystemNameBuffer.value
   704     
   705     def getNetworkDrive(self, vm_name):
   706         ip = self.getHostOnlyIP(vm_name)
   707         if ip == None:
   708             logger.error("Failed getting hostonly IP for " + vm_name)
   709             return None
   710         logger.info("Got IP address for " + vm_name + ': ' + ip)
   711         for drive in VMManager.getLogicalDrives():
   712             #if is a network drive
   713             if VMManager.getDriveType(drive) == 4:
   714                 network_path = VMManager.getNetworkPath(drive)
   715                 if ip in network_path:
   716                     return drive
   717         return None
   718     
   719     def getNetworkDrives(self):
   720         ip = self.getHostOnlyIP(None)
   721         if ip == None:
   722             logger.error("Failed getting hostonly IP for system")
   723             return None
   724         logger.info("Got IP address for system: " + ip)
   725         ip = ip[:ip.rindex('.')]
   726         network_drives = dict()
   727         for drive in VMManager.getLogicalDrives():
   728             #if is a network drive
   729             if VMManager.getDriveType(drive) == 4:
   730                 network_path = VMManager.getNetworkPath(drive)
   731                 if ip in network_path:
   732                     network_drives[drive] = network_path  
   733         return network_drives
   734     
   735     # handles browsing request    
   736     def handleBrowsingRequest(self, proxy):
   737         showTrayMessage('Starting Secure Browsing...', 7000)
   738         handler = BrowsingHandler(self, proxy)
   739         handler.start()
   740         return 'ok'
   741     
   742     def getActiveUserName(self):
   743         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI')
   744         v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0])
   745         win32api.RegCloseKey(key)
   746         user_name = win32api.ExpandEnvironmentStrings(v)
   747         return user_name
   748         
   749     def getUserSID(self, user_name):
   750         domain, user = user_name.split("\\")
   751         account_name = win32security.LookupAccountName(domain, user)
   752         if account_name == None:
   753             logger.error("Failed lookup account name for user " + user_name)
   754             return None
   755         sid = win32security.ConvertSidToStringSid(account_name[0])
   756         if sid == None:
   757             logger.error("Failed converting SID for account " + account_name[0])
   758             return None
   759         return sid
   760         
   761     def getAppDataDir(self, sid):    
   762         key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
   763         value, type = win32api.RegQueryValueEx(key, "AppData")
   764         win32api.RegCloseKey(key)
   765         return value
   766         
   767         #key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + '\\' + sid)
   768         #value, type = win32api.RegQueryValueEx(key, "ProfileImagePath")
   769         #print value
   770     
   771     def backupFile(self, src, dest):
   772         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   773         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + self.browsingManager.ip_addr + ':' + src + '" "' + dest + '"'
   774         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   775     
   776     def restoreFile(self, src, dest):
   777         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   778         #command = '-r -v -o StrictHostKeyChecking=no -i \"' + certificate + '\" \"' + src + '\" \"osecuser@' + self.browsingManager.ip_addr + ':' + dest + '\"'
   779         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + self.browsingManager.ip_addr + ':' + dest + '"'
   780         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)    
   781 
   782 #handles browsing session creation 
   783 class BrowsingHandler(threading.Thread):
   784     vmm = None
   785     proxy = None
   786     def __init__(self, vmmanager, proxy):
   787         threading.Thread.__init__(self)
   788         self.vmm = vmmanager
   789         self.proxy = proxy
   790         
   791     def run(self):
   792         #browser = '\\\"/usr/bin/chromium; pidof dbus-launch | xargs kill\\\"'
   793         #browser = '\\\"/usr/bin/chromium\\\"'
   794         
   795         try:
   796             if self.proxy:
   797                 browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"'
   798             else:
   799                 browser = '\\\"/usr/bin/chromium\\\"'
   800             self.vmm.browsingManager.started.wait() 
   801             result = Cygwin.checkResult(Cygwin.sshExecuteX11(browser, self.vmm.browsingManager.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vmm.browsingManager.vm_name + '/dvm_key'))
   802             self.vmm.backupFile('/home/osecuser/.config/chromium', self.vmm.browsingManager.appDataDir + '/OpenSecurity/')
   803         except:
   804             logger.info("BrowsingHandler closing. Restarting browsing SDVM.")
   805 
   806         self.vmm.browsingManager.restart.set()
   807         
   808             
   809 # handles browsing Vm creation and destruction                    
   810 class BrowsingManager(threading.Thread):   
   811     vmm = None
   812     running = True
   813     restart = None
   814     ip_addr = None
   815     vm_name = None
   816     net_resource = None
   817     appDataDir = None
   818     
   819     def __init__(self, vmmanager):
   820         threading.Thread.__init__(self)
   821         self.vmm = vmmanager
   822         self.restart = threading.Event()
   823         self.started = threading.Event()
   824     
   825     def stop(self):
   826         self.running = False
   827         self.restart.set()   
   828      
   829     def run(self):
   830         while self.running:
   831             self.restart.clear()
   832             self.started.clear()
   833             
   834             if self.net_resource == None:
   835                 logger.info("Missing browsing SDVM's network share. Skipping disconnect")
   836             else:
   837                 try:
   838                     browsing_vm = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline()
   839                     self.net_resource = None
   840                 except urllib2.URLError:
   841                     logger.error("Network share disconnect failed. OpenSecurity Tray client not running.")
   842                     continue
   843             
   844             self.ip_addr = None
   845 
   846             if self.vm_name != None:
   847                 self.vmm.poweroffVM(self.vm_name)
   848                 self.vmm.removeVM(self.vm_name)
   849             
   850             try:
   851                 self.vm_name = self.vmm.newSDVM()
   852                 self.vmm.storageAttach(self.vm_name)
   853                 self.vmm.genCertificateISO(self.vm_name)
   854                 self.vmm.attachCertificateISO(self.vm_name)
   855                 
   856                 self.vmm.startVM(self.vm_name)
   857                 
   858                 self.ip_addr = self.vmm.waitStartup(self.vm_name, timeout_ms=30000)
   859                 if self.ip_addr == None:
   860                     logger.error("Failed to get ip address")
   861                     continue
   862                 else:
   863                     logger.info("Got IP address for " + self.vm_name + ' ' + self.ip_addr)
   864                 
   865                 try:
   866                     self.net_resource = '\\\\' + self.ip_addr + '\\Download'
   867                     result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource).readline()
   868                 except urllib2.URLError:
   869                     logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   870                     self.net_resource = None
   871                     continue
   872                 
   873                 user = self.vmm.getActiveUserName()
   874                 if user == None:
   875                     logger.error("Cannot get active user name")
   876                     continue
   877                 else:
   878                     logger.info('Got active user name ' + user)
   879                 sid = self.vmm.getUserSID(user)
   880                 if sid == None:
   881                     logger.error("Cannot get SID for active user")
   882                     continue
   883                 else:
   884                     logger.info("Got active user SID " + sid + " for user " + user)
   885                     
   886                 path = self.vmm.getAppDataDir(sid)
   887                 if path == None:
   888                     logger.error("Cannot get AppDataDir for active user")
   889                     continue
   890                 else:
   891                     logger.info("Got AppData dir for user " + user + ': ' + path)
   892                 
   893                 self.appDataDir = Cygwin.cygPath(path)
   894                 logger.info("Restoring browser settings in AppData dir " + self.appDataDir)
   895                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
   896                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity\\\"'))
   897                 # create chromium settings dir on local machine if not existing
   898                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity/chromium\\\"'))
   899                 # create chromium settings dir on remote machine if not existing
   900                 Cygwin.checkResult(Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key'))
   901                 #restore settings on vm
   902                 self.vmm.restoreFile(self.appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
   903                 self.started.set()
   904                 logger.info("Browsing SDVM running.")
   905                 self.restart.wait()
   906             except OpenSecurityException, e:
   907                 logger.error(''.join(e))
   908             except:
   909                 logger.error("Unexpected error: " + sys.exc_info()[0])
   910                 logger.error("BrowsingHandler failed. Cleaning up")
   911                 #self.running= False
   912                 
   913 class DeviceHandler(threading.Thread): 
   914     vmm = None
   915     existingRSDs = None
   916     attachedRSDs = None  
   917     running = True
   918     def __init__(self, vmmanger): 
   919         threading.Thread.__init__(self)
   920         self.vmm = vmmanger
   921  
   922     def stop(self):
   923         self.running = False
   924         
   925     def run(self):
   926         
   927         self.existingRSDs = dict()
   928         self.attachedRSDs = self.vmm.getAttachedRSDs()
   929         
   930         while self.running:
   931             tmp_rsds = self.vmm.getExistingRSDs()
   932             if tmp_rsds.keys() == self.existingRSDs.keys():
   933                 logger.debug("Nothing's changed. sleep(3)")
   934                 time.sleep(3)
   935                 continue
   936             
   937             showTrayMessage('System changed.\nEvaluating...', 7000)
   938             logger.info("Something's changed")
   939             tmp_attached = self.attachedRSDs     
   940             for vm_name in tmp_attached.keys():
   941                 if tmp_attached[vm_name] not in tmp_rsds.values():
   942                     ip = self.vmm.getHostOnlyIP(vm_name)
   943                     if ip == None:
   944                         logger.error("Failed getting hostonly IP for " + vm_name)
   945                         continue
   946                     try:
   947                         net_resource = '\\\\' + ip + '\\USB'
   948                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline()
   949                     except urllib2.URLError:
   950                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   951                         continue
   952                     
   953                     # detach not necessary as already removed from vm description upon disconnect
   954                     #self.vmm.detachRSD(vm_name, self.attachedRSDs[vm_name])
   955                     del self.attachedRSDs[vm_name]
   956                     self.vmm.poweroffVM(vm_name)
   957                     self.vmm.removeVM(vm_name)
   958                     #break
   959                     
   960             #create new vms for new devices if any
   961             new_ip = None
   962             for new_device in tmp_rsds.values():
   963                 showTrayMessage('Mounting device...', 7000)
   964                 if (self.attachedRSDs and False) or (new_device not in self.attachedRSDs.values()):
   965                     new_sdvm = self.vmm.newSDVM()
   966                     self.vmm.storageAttach(new_sdvm)
   967                     self.vmm.startVM(new_sdvm)
   968                     new_ip = self.vmm.waitStartup(new_sdvm, timeout_ms=30000)
   969                     if new_ip == None:
   970                         logger.error("Error getting IP address of SDVM. Cleaning up.")
   971                         self.vmm.poweroffVM(new_sdvm)
   972                         self.vmm.removeVM(new_sdvm)
   973                         continue
   974                     else:
   975                         logger.info("Got IP address for " + new_sdvm + ' ' + new_ip)
   976                     try:
   977                         self.vmm.attachRSD(new_sdvm, new_device)
   978                         self.attachedRSDs[new_sdvm] = new_device
   979                     except:
   980                         logger.info("RSD prematurely removed. Cleaning up.")
   981                         self.vmm.poweroffVM(new_sdvm)
   982                         self.vmm.removeVM(new_sdvm)
   983                         continue
   984                     try:
   985                         net_resource = '\\\\' + new_ip + '\\USB'
   986                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+net_resource).readline()
   987                     except urllib2.URLError:
   988                         logger.error("Network drive connect failed (tray client not accessible). Cleaning up.")
   989                         self.vmm.poweroffVM(new_sdvm)
   990                         self.vmm.removeVM(new_sdvm)
   991                         continue
   992                     
   993             self.existingRSDs = tmp_rsds