OpenSecurity/bin/vmmanager.pyw
changeset 79 617009c32da0
parent 78 23551f635ca9
child 81 84663db906d7
     1.1 --- a/OpenSecurity/bin/vmmanager.pyw	Fri Feb 21 11:04:04 2014 +0100
     1.2 +++ b/OpenSecurity/bin/vmmanager.pyw	Wed Feb 26 17:27:07 2014 +0100
     1.3 @@ -6,7 +6,6 @@
     1.4  import os
     1.5  import os.path
     1.6  from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
     1.7 -import subprocess
     1.8  import sys
     1.9  import re
    1.10  
    1.11 @@ -20,7 +19,8 @@
    1.12  import stat
    1.13  import tempfile
    1.14  from opensecurity_util import logger, setupLogger, OpenSecurityException
    1.15 -
    1.16 +import ctypes
    1.17 +import itertools
    1.18  DEBUG = True
    1.19  
    1.20  class VMManagerException(Exception):
    1.21 @@ -55,15 +55,14 @@
    1.22      startNotifications = list()
    1.23      _instance = None
    1.24      machineFolder = ''
    1.25 -    attachedRSDs = None  
    1.26 -    connectedRSDs = None
    1.27 +    rsdHandler = None
    1.28      
    1.29      def __init__(self):
    1.30          self.systemProperties = self.getSystemProperties()
    1.31          self.machineFolder = self.systemProperties["Default machine folder"]
    1.32          self.cleanup()
    1.33 -        self.attachedRSDs = self.getAttachedRSDs()
    1.34 -        self.connectedRSDs = self.getConnectedRSDS()
    1.35 +        self.rsdHandler = DeviceHandler(self)
    1.36 +        self.rsdHandler.start()
    1.37          return
    1.38      
    1.39      @staticmethod
    1.40 @@ -73,8 +72,12 @@
    1.41          return VMManager._instance
    1.42      
    1.43      def cleanup(self):
    1.44 -        self.unmapNetworkDrive('G:')
    1.45 -        self.unmapNetworkDrive('H:')
    1.46 +        if self.rsdHandler != None:
    1.47 +            self.rsdHandler.stop()
    1.48 +            self.rsdHandler.join()
    1.49 +        drives = self.getNetworkDrives()
    1.50 +        for drive in drives.keys():
    1.51 +            self.unmapNetworkDrive(drive)
    1.52          for vm in self.listSDVM():
    1.53              self.poweroffVM(vm)
    1.54              self.removeVM(vm)
    1.55 @@ -83,7 +86,7 @@
    1.56          self.startNotifications.append(ip)
    1.57      
    1.58      def isSDVMStarted(self, ip):
    1.59 -        return self.startNotifications.contains(ip)
    1.60 +        return ip in self.startNotifications
    1.61      
    1.62      # return hosty system properties
    1.63      def getSystemProperties(self):
    1.64 @@ -97,14 +100,6 @@
    1.65      # return the folder containing the guest VMs     
    1.66      def getMachineFolder(self):
    1.67          return self.machineFolder
    1.68 -    
    1.69 -    #list the hostonly IFs exposed by the VBox host
    1.70 -    def getHostOnlyIFs(self):
    1.71 -        result = Cygwin.vboxExecute('list hostonlyifs')[1]
    1.72 -        if result=='':
    1.73 -            return None
    1.74 -        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
    1.75 -        return props
    1.76  
    1.77      # list all existing VMs registered with VBox
    1.78      def listVM(self):
    1.79 @@ -258,13 +253,14 @@
    1.80          self.waitStartup('SecurityDVM')
    1.81          Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
    1.82          Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
    1.83 -        self.stopVM('SecurityDVM')
    1.84 -        #self.hibernateVM('SecurityDVM')
    1.85 +        #self.stopVM('SecurityDVM')
    1.86 +        self.hibernateVM('SecurityDVM')
    1.87          self.waitShutdown('SecurityDVM')
    1.88          self.storageDetach('SecurityDVM')
    1.89          self.changeStorageType(template_storage,'immutable')
    1.90          self.storageAttach('SecurityDVM')
    1.91 -        self.handleDeviceChange()
    1.92 +        self.rsdHandler = DeviceHandler(self)
    1.93 +        self.rsdHandler.start()
    1.94      
    1.95      #remove VM from the system. should be used on VMs returned by listSDVMs    
    1.96      def removeVM(self, vm_name):
    1.97 @@ -295,7 +291,7 @@
    1.98      # stop VM
    1.99      def hibernateVM(self, vm_name):
   1.100          logger.info('Sending shutdown signal to ' + vm_name)
   1.101 -        Cygwin.sshExecute( '"sudo hibernate-disk"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' )
   1.102 +        Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False )
   1.103              
   1.104      # poweroff VM
   1.105      def poweroffVM(self, vm_name):
   1.106 @@ -304,18 +300,31 @@
   1.107          logger.info('Powering off ' + vm_name)
   1.108          return Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   1.109      
   1.110 -    # return the hostOnly IP for a running guest
   1.111 +    #list the hostonly IFs exposed by the VBox host
   1.112 +    @staticmethod    
   1.113 +    def getHostOnlyIFs():
   1.114 +        result = Cygwin.vboxExecute('list hostonlyifs')[1]
   1.115 +        if result=='':
   1.116 +            return None
   1.117 +        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   1.118 +        return props
   1.119 +    
   1.120 +    # return the hostOnly IP for a running guest or the host
   1.121      @staticmethod    
   1.122      def getHostOnlyIP(vm_name):
   1.123 -        logger.info('Gettting hostOnly IP address ' + vm_name)
   1.124 -        result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   1.125 -        if result=='':
   1.126 -            return None
   1.127 -        result = result[1]
   1.128 -        if result.startswith('No value set!'):
   1.129 -            return None
   1.130 -        return result[result.index(':')+1:].strip()
   1.131 -    
   1.132 +        if vm_name == None:
   1.133 +            logger.info('Gettting hostOnly IP address for Host')
   1.134 +            return VMManager.getHostOnlyIFs()['IPAddress']
   1.135 +        else:
   1.136 +            logger.info('Gettting hostOnly IP address ' + vm_name)
   1.137 +            result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   1.138 +            if result=='':
   1.139 +                return None
   1.140 +            result = result[1]
   1.141 +            if result.startswith('No value set!'):
   1.142 +                return None
   1.143 +            return result[result.index(':')+1:].strip()
   1.144 +            
   1.145      # attach removable storage device to VM by provision of filter
   1.146      def attachRSD(self, vm_name, rsd_filter):
   1.147          return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision)
   1.148 @@ -370,72 +379,18 @@
   1.149          result = Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + self.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   1.150          return result
   1.151      
   1.152 -    
   1.153 -    handleDeviceChangeLock = threading.Lock()
   1.154 -    trigger = False
   1.155 -    # handles device change events
   1.156 -    def handleDeviceChange(self):
   1.157 -        if VMManager.handleDeviceChangeLock.acquire(True):
   1.158 -            #logger.debug("triggered")
   1.159 -            #VMManager.handleDeviceChangeLock.release()
   1.160 -            #return
   1.161 -            #destroy unused vms
   1.162 -            #diff = DictDiffer(self.connectedRSDs, tmp_conn)
   1.163 -            retry = 0
   1.164 -            while retry < 30:
   1.165 -                if self.getConnectedRSDS().keys() == self.connectedRSDs.keys():
   1.166 -                    logger.info("Nothing's changed. Waiting for VBox USB sub-system to update...")
   1.167 -                else:
   1.168 -                    self.connectedRSDs = self.getConnectedRSDS()
   1.169 -                    break
   1.170 -                time.sleep(1)
   1.171 -                retry+=1
   1.172 -            
   1.173 -            if retry == 30:
   1.174 -                VMManager.handleDeviceChangeLock.release()
   1.175 -                return None
   1.176 -
   1.177 -            logger.info("Something's changed")
   1.178 -            
   1.179 -            self.attachedRSDs = self.getAttachedRSDs()
   1.180 -            
   1.181 -            for vm_name in self.attachedRSDs.keys():
   1.182 -                if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
   1.183 -                    self.unmapNetworkDrive('h:')
   1.184 -                    #self.stopVM(vm_name)
   1.185 -                    self.detachRSD(vm_name)
   1.186 -                    self.poweroffVM(vm_name)
   1.187 -                    self.removeVM(vm_name)
   1.188 -            #create new vm for attached device if any
   1.189 -            self.attachedRSDs = self.getAttachedRSDs()
   1.190 -            self.connectedRSDs = self.getConnectedRSDS()
   1.191 -            
   1.192 -            new_ip = None
   1.193 -            for connected_device in self.connectedRSDs.values():
   1.194 -                if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   1.195 -                    new_sdvm = self.generateSDVMName()
   1.196 -                    self.createVM(new_sdvm)
   1.197 -                    self.storageAttach(new_sdvm)
   1.198 -                    self.attachRSD(new_sdvm, connected_device)
   1.199 -                    self.startVM(new_sdvm)
   1.200 -                    new_ip = self.waitStartup(new_sdvm)
   1.201 -                    
   1.202 -                    if new_ip != None:
   1.203 -                        self.mapNetworkDrive('h:', '\\\\' + new_ip + '\\USB', None, None)
   1.204 -                    #TODO: cleanup notifications somwhere else (eg. machine shutdown)
   1.205 -                    self.startNotifications.remove(new_ip)
   1.206 -            VMManager.handleDeviceChangeLock.release()
   1.207 -            return new_ip
   1.208 -    
   1.209      # wait for machine to come up
   1.210 -    def waitStartup(self, vm_name): 
   1.211 -        new_ip = None
   1.212 -        while new_ip == None:
   1.213 -            time.sleep(1)
   1.214 -            new_ip = VMManager.getHostOnlyIP(vm_name)
   1.215 -        while new_ip not in self.startNotifications:
   1.216 -            time.sleep(1)
   1.217 -        return new_ip
   1.218 +    def waitStartup(self, vm_name, timeout_ms = 30000):
   1.219 +        #result = Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP --timeout ' + str(timeout_ms) + ' --fail-on-timeout')
   1.220 +        
   1.221 +        result = Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout')
   1.222 +        #new_ip = VMManager.getHostOnlyIP(vm_name)
   1.223 +        #while new_ip == None:
   1.224 +        #    time.sleep(1)
   1.225 +        #    new_ip = VMManager.getHostOnlyIP(vm_name)
   1.226 +        #while not self.isSDVMStarted(new_ip):
   1.227 +        #    time.sleep(1)
   1.228 +        return VMManager.getHostOnlyIP(vm_name)
   1.229      
   1.230      # wait for machine to shutdown
   1.231      def waitShutdown(self, vm_name):
   1.232 @@ -483,7 +438,7 @@
   1.233              logger.info("Path not accessible: " + networkPath + " retrying")
   1.234              #return -1
   1.235      
   1.236 -        command = 'USE ' + drive + ' ' + networkPath
   1.237 +        command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
   1.238          if user != None:
   1.239              command += ' ' + password + ' /User' + user
   1.240      
   1.241 @@ -495,35 +450,119 @@
   1.242          return 1
   1.243      
   1.244      def unmapNetworkDrive(self, drive):
   1.245 -        if not os.path.exists(drive):
   1.246 -            return -1
   1.247 +        drives = self.getNetworkDrives()
   1.248 +        if drive not in drives.keys():
   1.249 +            return 1 
   1.250          result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES')
   1.251 -        if string.find(str(result), 'successfully',) == -1:
   1.252 +        if string.find(str(result[1]), 'successfully',) == -1:
   1.253              logger.error(result[2])
   1.254              return -1
   1.255          return 1
   1.256 +    
   1.257 +    def getNetworkDrives(self):
   1.258 +        ip = VMManager.getHostOnlyIP(None)
   1.259 +        ip = ip[:ip.rindex('.')]
   1.260 +        drives = dict()    
   1.261 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
   1.262 +        for line in result[1].splitlines():
   1.263 +            if ip in line:
   1.264 +                parts = line.split()
   1.265 +                drives[parts[1]] = parts[2]
   1.266 +        return drives
   1.267 +            
   1.268 +    def genNetworkDrive(self):
   1.269 +        network_drives = self.getNetworkDrives()
   1.270 +        logical_drives = self.getLogicalDrives()
   1.271 +        drives = list(map(chr, range(68, 91)))  
   1.272 +        for drive in drives:
   1.273 +            if drive+':' not in network_drives and drive not in logical_drives:
   1.274 +                return drive+':'
   1.275  
   1.276 +    def getNetworkDrive(self, vm_name):
   1.277 +        ip = self.getHostOnlyIP(vm_name)
   1.278 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
   1.279 +        for line in result[1].splitlines():
   1.280 +            if ip in line:
   1.281 +                parts = line.split()
   1.282 +                return parts[0]
   1.283 +    
   1.284 +    def getLogicalDrives(self):
   1.285 +        drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   1.286 +        return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   1.287 +                    
   1.288 +        #vms = self.listSDVM()
   1.289 +        #for vm in vms:
   1.290 +        #    ip = self.getHostOnlyIP(vm)
   1.291 +        
   1.292  class DeviceHandler(threading.Thread): 
   1.293      vmm = None
   1.294 -    triggered = False
   1.295 -    def __init__(self, zahl): 
   1.296 -        threading.Thread.__init__(self) 
   1.297 -        self.vmm = None 
   1.298 +    #handleDeviceChangeLock = threading.Lock()
   1.299 +    attachedRSDs = None  
   1.300 +    connectedRSDs = None
   1.301 +    running = True
   1.302 +    def __init__(self, vmmanger): 
   1.303 +        threading.Thread.__init__(self)
   1.304 +        self.vmm = vmmanger
   1.305   
   1.306 +    def stop(self):
   1.307 +        self.running = False
   1.308 +        
   1.309      def run(self):
   1.310 -        while True:
   1.311 -            if self.triggered:
   1.312 -                logger.debug("triggered")
   1.313 -                triggered = False
   1.314 -            else:
   1.315 -                time.sleep(1)
   1.316 +        self.connectedRSDs = dict()#self.vmm.getConnectedRSDS()
   1.317 +        self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.318 +        while self.running:
   1.319 +            tmp_rsds = self.vmm.getConnectedRSDS()
   1.320 +            if tmp_rsds.keys() == self.connectedRSDs.keys():
   1.321 +                logger.debug("Nothing's changed. sleep(3)")
   1.322 +                time.sleep(3)
   1.323 +                continue
   1.324 +            
   1.325 +            logger.info("Something's changed")          
   1.326 +            
   1.327 +            self.connectedRSDs = tmp_rsds
   1.328 +            self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.329 +            
   1.330 +            
   1.331 +            for vm_name in self.attachedRSDs.keys():
   1.332 +                if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
   1.333 +                    drive = self.vmm.getNetworkDrive(vm_name)
   1.334 +                    self.vmm.unmapNetworkDrive(drive)
   1.335 +                    #self.stopVM(vm_name)
   1.336 +                    self.vmm.detachRSD(vm_name)
   1.337 +                    self.vmm.poweroffVM(vm_name)
   1.338 +                    self.vmm.removeVM(vm_name)
   1.339 +            #create new vm for attached device if any
   1.340 +            self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.341 +            self.connectedRSDs = self.vmm.getConnectedRSDS()
   1.342 +            
   1.343 +            new_ip = None
   1.344 +            for connected_device in self.connectedRSDs.values():
   1.345 +                if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   1.346 +                    new_sdvm = self.vmm.generateSDVMName()
   1.347 +                    self.vmm.createVM(new_sdvm)
   1.348 +                    self.vmm.storageAttach(new_sdvm)
   1.349 +                    self.vmm.attachRSD(new_sdvm, connected_device)
   1.350 +                    self.vmm.startVM(new_sdvm)
   1.351 +                    new_ip = self.vmm.waitStartup(new_sdvm)
   1.352 +                    drive = self.vmm.genNetworkDrive()
   1.353 +                    if new_ip != None:
   1.354 +                        self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
   1.355 +                    #TODO: cleanup notifications somwhere else (eg. machine shutdown)
   1.356 +                    self.vmm.startNotifications.remove(new_ip)
   1.357 +            #self.handleDeviceChangeLock.release()
   1.358                  
   1.359  
   1.360  if __name__ == '__main__':
   1.361 -    man = VMManager.getInstance()
   1.362 +    #man = VMManager.getInstance()
   1.363      #man.listVM()
   1.364 -    print man.getConnectedRSDs()
   1.365 +    #print man.getConnectedRSDs()
   1.366 +    #print man.getNetworkDrives()
   1.367 +    #man.genNetworkDrive()
   1.368 +    drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   1.369 +    print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   1.370 +    #print list(map(chr, range(68, 91))) 
   1.371      
   1.372 +    #time.sleep(-1)
   1.373      #man.listVM()
   1.374      #man.listVM()
   1.375      #man.listVM()
   1.376 @@ -531,7 +570,7 @@
   1.377      #man.genCertificateISO('SecurityDVM0')
   1.378      #man.guestExecute('SecurityDVM0', '/bin/ls -la')
   1.379      #logger = setupLogger('VMManager')
   1.380 -    c = Cygwin()
   1.381 +    #c = Cygwin()
   1.382      
   1.383      #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
   1.384      #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')