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