def xmlrpc_logClientAction(self, mac, level, phase, message): """ Remote logging. Mainly used to send progress info to our mmc-agent. @param mac : The mac address of the client @param level : the log level @param phase : what the client was doing when the info was logged @param message : the log message itself @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _getmacCB(result): if result and type(result) == dict : client = self._getXMLRPCClient() func = 'imaging.logClientAction' args = (self.config.imaging_api['uuid'], result['uuid'], level, phase, message) d = client.callRemote(func, *args) d.addCallbacks(lambda x : True, RPCReplay().onError, errbackArgs = (func, args, 0)) return d self.logger.warn('Imaging: Failed resolving UUID for client %s : %s' % (mac, result)) return False if not isMACAddress(mac): raise TypeError self.logger.debug('Imaging: Client %s sent a log message while %s (%s) : %s' % (mac, phase, level, message)) d = self.xmlrpc_getComputerByMac(mac) d.addCallback(_getmacCB) return d
def _validate_target (self, target): """ Validating of target format @param target: target container @type target: list @return: True if format is valid @rtype: bool """ if not isinstance(target, tuple) or not isinstance(target, list): log.warn("Invalid target format.") return False if len(target) != 3 : log.warn("Invalid target format.") return False hostname, fqdn, ifaces = target if not isinstance(ifaces, dict) : log.warn("Invalid target format.") return False for iface in ifaces : for key, value in iface.items(): if not isinstance(value, str) : log.warn("Invalid interface format, section '%s' (hostname='%s')" % (key, hostname)) return False if key == "mac" : if not isMACAddress(value) : log.warn("Invalid MAC address format : '%s' (hostname='%s')" % (value, hostname)) return False return True
def computerRegister(self, computerName, MACAddress, imagingData, waitToBeInventoried=False): """ Called by pulse2-imaging-server to tell the Package Server to register a new computer. The computer name may contain a profile and an entity path (self,like profile:/entityA/entityB/computer) @type computerName: str @type MACAddress: str @raise : TypeError is computerName is not a str @raise : TypeError is MACAddress is not a mac addr @rtype : bool """ if type(computerName) != str and type(computerName) != unicode: raise TypeError('Bad Computer name: %s' % computerName) if not isMACAddress(MACAddress): raise TypeError('BAD MAC address: %s' % MACAddress) d = self.callRemote("computerRegister", computerName, MACAddress, imagingData, waitToBeInventoried) d.addErrback(self.onErrorRaise, "Imaging:computerRegister", [computerName, MACAddress, imagingData, waitToBeInventoried]) return d
def POST(self, authkey, mac_list, hostname): log("Authenticate computer %s with authkey %s" % (hostname, authkey)) if not authkey == cherrypy.config.get("dlp.authkey"): log("Failed to authenticate computer %s, authkey missmatch." % hostname, severity=logging.ERROR) raise cherrypy.HTTPError(401, "Not authorized") if isinstance(mac_list, basestring): mac_list = [mac_list] # Validate input values for mac in mac_list: if not isMACAddress(mac): raise cherrypy.HTTPError(400, "MAC address is not correct") try: uuid = cherrypy.request.xmlrpc_client.pull_target_awake(hostname, mac_list) if uuid is not None and uuid: cherrypy.session[HOSTNAME_KEY] = hostname cherrypy.session[MAC_KEY] = mac_list cherrypy.session[UUID_KEY] = uuid cherrypy.session.save() log("Result: %s" % uuid) return "OK" else: log("Not recognized machine, hostname=%s, mac_list=%s" % (hostname, mac_list), severity=logging.WARNING, traceback=True) raise cherrypy.HTTPError(404, "Not found") except cherrypy.HTTPError: raise except: log("pull_target_awake failed\n", severity=logging.ERROR, traceback=True) raise cherrypy.HTTPError(503, "Service unavailable")
def computerUpdate(self, MACAddress): """ Method to update the menu a computer. @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if update was successful, else 0. @rtype: int """ def onSuccess(result): # TODO : add menu re-generation here return 1 if not isMACAddress(MACAddress): raise TypeError url, credentials = makeURL(PackageServerConfig().mmc_agent) self.logger.info('Imaging: Starting menu update for %s' % (MACAddress)) client = self._getXMLRPCClient() func = 'imaging.getMenu' args = (MACAddress) d = client.callRemote(func, *args) d.addCallbacks(onSuccess, client.onError, errbackArgs = (func, args, 0)) return d
def computerUpdate(self, MACAddress): """ Method to update the menu a computer. @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if update was successful, else 0. @rtype: int """ def onSuccess(result): # TODO : add menu re-generation here return 1 if not isMACAddress(MACAddress): raise TypeError url, credentials = makeURL(PackageServerConfig().mmc_agent) self.logger.info('Imaging: Starting menu update for %s' % (MACAddress)) client = self._getXMLRPCClient() func = 'imaging.getMenu' args = (MACAddress) d = client.callRemote(func, *args) d.addCallbacks(onSuccess, client.onError, errbackArgs=(func, args, 0)) return d
def xmlrpc_computerChangeDefaultMenuItem(self, mac, num): """ Called by computer MAC when he want to set its default entry to num @param mac : The mac address of the client @param num : The menu number @type mac: MAC Address @type num: int """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error( 'Couldn\'t set default entry on %s for %s : %s' % (num, computerUUID, str(result))) ret = False elif not result[0]: self.logger.error( 'Couldn\'t set default entry on %s for %s : %s' % (num, computerUUID, str(result))) ret = False else: self.logger.info( 'Successfully set default entry on %s for %s' % (num, computerUUID)) ret = True return ret def _onError(error, funcname, args, default_return): # Storing RPC so that it can be replayed lated RPCReplay().onError(error, funcname, args, default_return) # Manually update the computer boot menu self.logger.warning( 'MMC agent can\'t be contacted: updating default menu item to the first "manually"' ) if changeDefaultMenuItem(mac, 0): self.logger.warning('Update done') else: self.logger.error('Update failed') return default_return if not isMACAddress(mac): self.logger.error("Bad computer MAC %s" % str(mac)) ret = False else: computerUUID = self.myUUIDCache.getByMac(mac) if not computerUUID: self.logger.error( "Can't get computer UUID for MAC address %s" % mac) ret = False else: computerUUID = computerUUID['uuid'] client = self._getXMLRPCClient() func = 'imaging.computerChangeDefaultMenuItem' args = (self.config.imaging_api['uuid'], computerUUID, num) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, _onError, errbackArgs=(func, args, True)) return d return ret
def _validate_target (self, target): """ Validating of target format @param target: target container @type target: list @return: True if format is valid @rtype: bool """ if not isinstance(target, tuple) or not isinstance(target, list): log.warn("Invalid target format.") return False if len(target) != 2 : log.warn("Invalid target format.") return False hostname, ifaces = target if not isinstance(ifaces, dict) : log.warn("Invalid target format.") return False for iface in ifaces : for key, value in iface.items(): if not isinstance(value, str) : log.warn("Invalid interface format, section '%s' (hostname='%s')" % (key, hostname)) return False if key == "mac" : if not isMACAddress(value) : log.warn("Invalid MAC address format : '%s' (hostname='%s')" % (value, hostname)) return False return True
def computerCreateImageDirectory(self, MACAddress): """ Asks the Package Server to create the file system structure for the given computer uuid thanks to imagingData content. """ if not isMACAddress(MACAddress): raise TypeError d = self.callRemote("computerCreateImageDirectory", MACAddress) d.addErrback(self.onErrorRaise, "Imaging:computerCreateImageDirectory", [MACAddress]) return d
def computerPrepareImagingDirectory(self, MACAddress, imagingData=False): """ Asks the Package Server to create the file system structure for the given computer uuid thanks to imagingData content. If imagingData is False, the package server queries the MMC agent for the imaging data. """ if not isMACAddress(MACAddress): raise TypeError d = self.callRemote("computerPrepareImagingDirectory", MACAddress, imagingData) d.addErrback(self.onErrorRaise, "Imaging:computerPrepareImagingDirectory", [MACAddress, imagingData]) return d
def computerRegister(self, computerName, macAddress, imagingData=False, waitToBeInventoried=False): """ Method to register a new computer. if imagingData is set, we know that the method is called from a MMC agent ! @raise TypeError: if computerName or MACAddress are malformed @return: a deferred object resulting to True if registration was successful, else False. @rtype: bool """ def onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error( 'Imaging: Registering client %s (%s) failed: %s' % (computerName, macAddress, str(result))) ret = False elif not result[0]: self.logger.error( 'Imaging: Registering client %s (%s) failed: %s' % (computerName, macAddress, result[1])) ret = False else: uuid = result[1] self.logger.info('Imaging: Client %s (%s) registered as %s' % (computerName, macAddress, uuid)) self.myUUIDCache.set(uuid, macAddress, hostname, domain, entity) ret = self.computerPrepareImagingDirectory( uuid, { 'mac': macAddress, 'hostname': hostname }) return ret try: # check MAC Addr is conform if not isMACAddress(macAddress): raise TypeError('Malformed MAC address: %s' % macAddress) # check computer name is conform if not len(computerName): raise TypeError('Malformed computer name: %s' % computerName) profile, entity_path, hostname, domain = splitComputerPath( computerName) entity_path = entity_path.split('/') entity = entity_path.pop() except TypeError, ex: self.logger.error('Imaging: Won\'t register %s as %s : %s' % (macAddress, computerName, ex)) return maybeDeferred(lambda x: x, False)
def computerChangeDefaultMenuItem(self, mac, num): """ Called by computer MAC when he want to set its default entry to num @param mac : The mac address of the client @param num : The menu number @type mac: MAC Address @type num: int """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error('Couldn\'t set default entry on %s for %s : %s' % (num, computerUUID, str(result))) ret = False elif not result[0]: self.logger.error('Couldn\'t set default entry on %s for %s : %s' % (num, computerUUID, str(result))) ret = False else: shortname = self.getClientShortname(mac) if shortname: self.logger.info('Default entry set to %s after disk image creation/restoration for client %s (%s)' % (num, shortname, mac)) else: self.logger.info('Default entry set to %s after disk image creation/restoration for unknown client (%s)' % (num, mac)) ret = True return ret def _onError(error, funcname, args, default_return): # Storing RPC so that it can be replayed lated RPCReplay().onError(error, funcname, args, default_return) # Manually update the computer boot menu self.logger.warning('MMC agent can\'t be contacted: updating default menu item to the first "manually"') if changeDefaultMenuItem(mac, 0): self.logger.warning('Update done') else: self.logger.error('Update failed') return default_return if not isMACAddress(mac): self.logger.error("Bad computer MAC %s" % str(mac)) ret = False else: computerUUID = self.myUUIDCache.getByMac(mac) if not computerUUID: self.logger.error("Can't get computer UUID for MAC address %s" % mac) ret = False else: computerUUID = computerUUID['uuid'] client = self._getXMLRPCClient() func = 'imaging.computerChangeDefaultMenuItem' args = (self.config.imaging_api['uuid'], computerUUID, num) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, _onError, errbackArgs = (func, args, True)) return d return ret
def mac(self): """ Common argument for all PXE methods """ if self.MAC_FLAG in self.packet: start = self.packet.index(self.MAC_FLAG) + len(self.MAC_FLAG) if len(self.packet[start:]) >= 17: mac = self.packet[start:start + 17] if isMACAddress(mac): return mac return None
def mac(self): """ Common argument for all PXE methods """ if self.MAC_FLAG in self.packet : start = self.packet.index(self.MAC_FLAG) + len(self.MAC_FLAG) if len(self.packet[start:]) >= 17 : mac = self.packet[start:start+17] if isMACAddress(mac) : return mac return None
def xmlrpc_injectInventory(self, MACAddress, inventory): """ Method to process the inventory of a computer @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _onSuccess(result): if result and type(result) == list and len(result) == 2: if result[0] == True: self.logger.info( 'Imaging: injected inventory for client %s' % (MACAddress)) return True else: self.logger.warn( 'Imaging: failed injecting inventory for client %s : %s' % (MACAddress, result[1])) return False else: self.logger.warn( 'Imaging: failed injecting inventory for client %s : %s' % (MACAddress, result)) return False def _getmacCB(result): if result and type(result) == dict: client = self._getXMLRPCClient() func = 'imaging.injectInventory' args = (self.config.imaging_api['uuid'], result['uuid'], inventory) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, client.onError, errbackArgs=(func, args, 0)) return d self.logger.warn( 'Imaging: Failed resolving UUID for client %s : %s' % (MACAddress, result)) return False if not isMACAddress(MACAddress): raise TypeError self.logger.debug('Imaging: Starting inventory processing for %s' % (MACAddress)) d = self.xmlrpc_getComputerByMac(MACAddress) d.addCallback(_getmacCB) return d
def injectInventory(self, MACAddress, inventory): """ Method to process the inventory of a computer @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _onSuccess(result): shortname = self.getClientShortname(MACAddress) if result and type(result) == list and len(result) == 2: if result[0] == True : if shortname: self.logger.debug('Imaging: Imaging database disks and partitions information successfully updated for client %s (%s)' % (shortname, MACAddress)) else: self.logger.debug('Imaging: Imaging database disks and partitions information successfully updated for unknown client (%s)' % (MACAddress)) return True else: if shortname: self.logger.error('Imaging: Failed to update disks and partitions information for client %s (%s): %s' % (shortname, MACAddress, result[1])) else: self.logger.error('Imaging: Failed to update disks and partitions information for unknown client (%s): %s' % (MACAddress, result[1])) return False else: if shortname: self.logger.error('Imaging: Failed to update disks and partitions information for client %s (%s): %s' % (shortname, MACAddress, result)) else: self.logger.error('Imaging: Failed to update disks and partitions information for unknown client (%s): %s' % (MACAddress, result)) return False def _getmacCB(result): if result and type(result) == dict: client = self._getXMLRPCClient() func = 'imaging.injectInventory' args = (self.config.imaging_api['uuid'], result['uuid'], inventory) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, client.onError, errbackArgs=(func, args, 0)) return d return False if not isMACAddress(MACAddress): raise TypeError self.logger.debug('Imaging: New PXE inventory received from client %s' % (MACAddress)) d = self.getComputerByMac(MACAddress) d.addCallback(_getmacCB) return d
def getDefaultMenuItem(self, mac): """ Getting of default menu entry from the database. @param mac: MAC address of machine @type mac: str @return: default menu entry @rtype: int """ if not isMACAddress(mac): self.logger.error("Get default menu item: bad computer MAC %s" % str(mac)) ret = False else: computerUUID = self.myUUIDCache.getByMac(mac) if not computerUUID: self.logger.error( "Can't get computer UUID for MAC address %s" % mac) ret = False else: computerUUID = computerUUID['uuid'] client = self._getXMLRPCClient() func = 'imaging.getDefaultMenuItem' d = client.callRemote(func, computerUUID) @d.addCallback def _cb(result): if isinstance(result, list): success, order = result if success: shortname = self.getClientShortname(mac) if shortname: self.logger.debug( "Client %s (%s) default menu entry is %s" % (shortname, str(mac), order)) else: self.logger.debug( "Unknown client (%s) default menu entry is %s" % (shortname, str(mac), order)) return order return d return ret
def xmlrpc_logClientAction(self, mac, level, phase, message): """ Remote logging. Mainly used to send progress info to our mmc-agent. @param mac : The mac address of the client @param level : the log level @param phase : what the client was doing when the info was logged @param message : the log message itself @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _getmacCB(result): if result and type(result) == dict: client = self._getXMLRPCClient() func = 'imaging.logClientAction' args = (self.config.imaging_api['uuid'], result['uuid'], level, phase, message) d = client.callRemote(func, *args) d.addCallbacks(lambda x: True, RPCReplay().onError, errbackArgs=(func, args, 0)) return d self.logger.warn( 'Imaging: Failed resolving UUID for client %s : %s' % (mac, result)) return False if not isMACAddress(mac): raise TypeError self.logger.debug( 'Imaging: Client %s sent a log message while %s (%s) : %s' % (mac, phase, level, message)) d = self.xmlrpc_getComputerByMac(mac) d.addCallback(_getmacCB) return d
def computerMenuUpdate(self, mac): """ Perform a menu refresh. @param mac : The mac address of the client @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _getmacCB(result): if result and type(result) == dict: # TODO : call menu refresh here return True return False if not isMACAddress(mac): raise TypeError self.logger.debug('Imaging: Client %s asked for a menu update' % (mac)) d = self.getComputerByMac(mac) d.addCallback(_getmacCB) return d
def computerRegister(self, computerName, macAddress, imagingData = False, waitToBeInventoried=False): """ Method to register a new computer. if imagingData is set, we know that the method is called from a MMC agent ! @raise TypeError: if computerName or MACAddress are malformed @return: a deferred object resulting to True if registration was successful, else False. @rtype: bool """ def onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error('Imaging: Registering client %s (%s) failed: %s' % (computerName, macAddress, str(result))) ret = False elif not result[0]: self.logger.error('Imaging: Registering client %s (%s) failed: %s' % (computerName, macAddress, result[1])) ret = False else: uuid = result[1] self.logger.info('Imaging: Client %s (%s) registered as %s' % (computerName, macAddress, uuid)) self.myUUIDCache.set(uuid, macAddress, hostname, domain, entity) ret = self.computerPrepareImagingDirectory(uuid, {'mac': macAddress, 'hostname': hostname}) return ret try: # check MAC Addr is conform if not isMACAddress(macAddress): raise TypeError('Malformed MAC address: %s' % macAddress) # check computer name is conform if not len(computerName): raise TypeError('Malformed computer name: %s' % computerName) profile, entity_path, hostname, domain = splitComputerPath(computerName) entity_path = entity_path.split('/') entity = entity_path.pop() except TypeError, ex: self.logger.error('Imaging: Won\'t register %s as %s : %s' % (macAddress, computerName, ex)) return maybeDeferred(lambda x: x, False)
def POST(self, authkey, mac_list, hostname): log("Authenticate computer %s with authkey %s" % (hostname, authkey)) if not authkey == cherrypy.config.get("dlp.authkey"): log("Failed to authenticate computer %s, authkey missmatch." % hostname, severity=logging.ERROR) raise cherrypy.HTTPError(401, "Not authorized") if isinstance(mac_list, basestring): mac_list = [mac_list] # Validate input values for mac in mac_list: if not isMACAddress(mac): raise cherrypy.HTTPError(400, "MAC address is not correct") try: uuid = cherrypy.request.xmlrpc_client.pull_target_awake( hostname, mac_list) if uuid is not None and uuid: cherrypy.session[HOSTNAME_KEY] = hostname cherrypy.session[MAC_KEY] = mac_list cherrypy.session[UUID_KEY] = uuid cherrypy.session.save() log("Result: %s" % uuid) return "OK" else: log("Not recognized machine, hostname=%s, mac_list=%s" % (hostname, mac_list), severity=logging.WARNING, traceback=True) raise cherrypy.HTTPError(404, "Not found") except cherrypy.HTTPError: raise except: log("pull_target_awake failed\n", severity=logging.ERROR, traceback=True) raise cherrypy.HTTPError(503, "Service unavailable")
def computerMenuUpdate(self, mac): """ Perform a menu refresh. @param mac : The mac address of the client @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _getmacCB(result): if result and type(result) == dict : # TODO : call menu refresh here return True return False if not isMACAddress(mac): raise TypeError self.logger.debug('Imaging: Client %s asked for a menu update' % (mac)) d = self.getComputerByMac(mac) d.addCallback(_getmacCB) return d
def xmlrpc_injectInventory(self, MACAddress, inventory): """ Method to process the inventory of a computer @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _onSuccess(result): if result and type(result) == list and len(result) == 2: if result[0] == True : self.logger.info('Imaging: injected inventory for client %s' % (MACAddress)) return True else: self.logger.warn('Imaging: failed injecting inventory for client %s : %s' % (MACAddress, result[1])) return False else: self.logger.warn('Imaging: failed injecting inventory for client %s : %s' % (MACAddress, result)) return False def _getmacCB(result): if result and type(result) == dict: client = self._getXMLRPCClient() func = 'imaging.injectInventory' args = (self.config.imaging_api['uuid'], result['uuid'], inventory) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, client.onError, errbackArgs=(func, args, 0)) return d self.logger.warn('Imaging: Failed resolving UUID for client %s : %s' % (MACAddress, result)) return False if not isMACAddress(MACAddress): raise TypeError self.logger.debug('Imaging: Starting inventory processing for %s' % (MACAddress)) d = self.xmlrpc_getComputerByMac(MACAddress) d.addCallback(_getmacCB) return d
def getDefaultMenuItem(self, mac): """ Getting of default menu entry from the database. @param mac: MAC address of machine @type mac: str @return: default menu entry @rtype: int """ if not isMACAddress(mac): self.logger.error("Get default menu item: bad computer MAC %s" % str(mac)) ret = False else: computerUUID = self.myUUIDCache.getByMac(mac) if not computerUUID: self.logger.error("Can't get computer UUID for MAC address %s" % mac) ret = False else: computerUUID = computerUUID['uuid'] client = self._getXMLRPCClient() func = 'imaging.getDefaultMenuItem' d = client.callRemote(func, computerUUID) @d.addCallback def _cb(result): if isinstance(result, list) : success, order = result if success : shortname = self.getClientShortname(mac) if shortname: self.logger.debug("Client %s (%s) default menu entry is %s" % (shortname, str(mac), order)) else: self.logger.debug("Unknown client (%s) default menu entry is %s" % (shortname, str(mac), order)) return order return d return ret
def injectInventory(self, MACAddress, inventory): """ Method to process the inventory of a computer @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _onSuccess(result): shortname = self.getClientShortname(MACAddress) if result and type(result) == list and len(result) == 2: if result[0] == True: if shortname: self.logger.debug( 'Imaging: Imaging database disks and partitions information successfully updated for client %s (%s)' % (shortname, MACAddress)) else: self.logger.debug( 'Imaging: Imaging database disks and partitions information successfully updated for unknown client (%s)' % (MACAddress)) return True else: if shortname: self.logger.error( 'Imaging: Failed to update disks and partitions information for client %s (%s): %s' % (shortname, MACAddress, result[1])) else: self.logger.error( 'Imaging: Failed to update disks and partitions information for unknown client (%s): %s' % (MACAddress, result[1])) return False else: if shortname: self.logger.error( 'Imaging: Failed to update disks and partitions information for client %s (%s): %s' % (shortname, MACAddress, result)) else: self.logger.error( 'Imaging: Failed to update disks and partitions information for unknown client (%s): %s' % (MACAddress, result)) return False def _getmacCB(result): if result and type(result) == dict: client = self._getXMLRPCClient() func = 'imaging.injectInventory' args = (self.config.imaging_api['uuid'], result['uuid'], inventory) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, client.onError, errbackArgs=(func, args, 0)) return d return False if not isMACAddress(MACAddress): raise TypeError self.logger.debug( 'Imaging: New PXE inventory received from client %s' % (MACAddress)) d = self.getComputerByMac(MACAddress) d.addCallback(_getmacCB) return d
def test_MACValid(self): self.assertTrue(isMACAddress('00:11:aa:BB:22:33')) self.assertTrue(isMACAddress(u'00:11:aa:BB:22:33'))
def logClientAction(self, mac, level, phase, message): """ Remote logging. Mainly used to send progress info to our mmc-agent. @param mac : The mac address of the client @param level : the log level @param phase : what the client was doing when the info was logged @param message : the log message itself @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _infoLogs(message): """ Display Some info logs: * When a machine is booting * When a restoration is done """ shortname = self.getClientShortname(mac) if 'booted' in message: if shortname: self.logger.info('Imaging: Client %s (%s) has booted' % (shortname, mac)) else: self.logger.info( 'Imaging: Unknown client (%s) has booted' % (mac)) elif 'restoration started' in message: # image UUID is in the 36 last characters of message variable # message[-37:-1] to get image UUID if shortname: self.logger.info( 'Imaging: Client %s (%s) is restoring a disk image (%s)' % (shortname, mac, message[-37:-1])) else: self.logger.info( 'Imaging: Unknown client (%s) is restoring a disk image (%s)' % (mac, message[-37:-1])) elif 'restoration finished' in message: # image UUID is in the 36 last characters of message variable # message[-37:-1] to get image UUID if shortname: self.logger.info( 'Imaging: Disk image (%s) restored successfully to client %s (%s)' % (message[-37:-1], shortname, mac)) else: self.logger.info( 'Imaging: Disk image (%s) restored successfully to unknown client (%s)' % (message[-37:-1], mac)) def _getmacCB(result): displayed_statuses = [ 'booted', 'restoration started', 'restoration finished' ] if result and type(result) == dict: if any(s in message for s in displayed_statuses): _infoLogs(message) # Display some info logs client = self._getXMLRPCClient() func = 'imaging.logClientAction' args = (self.config.imaging_api['uuid'], result['uuid'], level, phase, message) d = client.callRemote(func, *args) d.addCallbacks(lambda x: True, RPCReplay().onError, errbackArgs=(func, args, 0)) return d if any(s in message for s in displayed_statuses): _infoLogs(message) # Display some info logs return False if not isMACAddress(mac): raise TypeError self.logger.debug( 'Imaging: Client %s sent a log message while %s (%s) : %s' % (mac, phase, level, message)) d = self.getComputerByMac(mac) d.addCallback(_getmacCB) return d
def imageDone(self, computerMACAddress, imageUUID): """ Called by the imaging server to register a new image. @return: True if successful @rtype: bool """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error( 'Imaging: Couldn\'t update on the MMC agent the image with UUID %s : %s' % (imageUUID, str(result))) ret = False elif not result[0]: self.logger.error( 'Imaging: Couldn\'t update on the MMC agent the image with UUID %s : %s' % (imageUUID, result[1])) ret = False else: image_path = os.path.join( self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], imageUUID) shortname = self.getClientShortname(computerMACAddress) if shortname: self.logger.info( 'Imaging: Disk image of client %s (%s) created successfully into %s' % (shortname, computerMACAddress, image_path)) else: self.logger.info( 'Imaging: Disk image of unknown client (%s) created successfully into %s' % (computerMACAddress, image_path)) ret = True return ret def _gotMAC(result): """ Process result return by getComputerByMac, BTW should be either False or the required info """ if not result: self.logger.error( "Can't get computer UUID for MAC address %s" % (computerMACAddress)) return False else: # start gathering details about our image path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], imageUUID) image = Pulse2Image(path) if not image: state = "INVALID" size = 0 # FIXME : use actual size name = "Failed backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) elif image.has_error: state = "FAILED" size = image.size name = "Backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) else: state = "DONE" size = image.size name = "Backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) c_uuid = result['uuid'] updateDate = tuple(gmtime()) client = self._getXMLRPCClient() func = 'imaging.imageUpdate' # size converted to float to bypass xmlrpc limitations args = (self.config.imaging_api['uuid'], c_uuid, imageUUID, name, desc, float(size), updateDate, state) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, RPCReplay().onError, errbackArgs=(func, args, False)) return True if not isUUID(imageUUID): self.logger.error("Bad image UUID %s" % str(imageUUID)) ret = False elif not isMACAddress(computerMACAddress): self.logger.error("Bad computer MAC %s" % str(computerMACAddress)) ret = False else: d = self.getComputerByMac(computerMACAddress) d.addCallback(_gotMAC) return d return ret
result[1]['fqdn'].encode('utf-8'), result[1]['entity'].encode('utf-8')) self.logger.debug('Imaging: Updating cache for %s' % (MACAddress)) return result[1] else: self.logger.debug( "Imaging: Unable to resolve %s neither from cache nor from database (unknown computer?)" % (MACAddress)) return False except Exception, e: self.logger.error( 'Imaging: While processing result %s for %s : %s' % (result, MACAddress, e)) if not isMACAddress(MACAddress): raise TypeError('Bad MAC address: %s' % MACAddress) # try to extract from our cache res = self.myUUIDCache.getByMac(MACAddress) if res: # fetched from cache res['shortname'] = res['shortname'].decode('utf-8') res['fqdn'] = res['fqdn'].decode('utf-8') res['entity'] = res['entity'].decode('utf-8') return maybeDeferred(lambda x: x, res) else: # cache fetching failed, try to obtain the real value self.logger.debug( 'Imaging: Unable to resolve %s from cache, querying database' % (MACAddress)) client = self._getXMLRPCClient() func = 'imaging.getComputerByMac'
def imageDone(self, computerMACAddress, imageUUID): """ Called by the imaging server to register a new image. @return: True if successful @rtype: bool """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error('Imaging: Couldn\'t update on the MMC agent the image with UUID %s : %s' % (imageUUID, str(result))) ret = False elif not result[0]: self.logger.error('Imaging: Couldn\'t update on the MMC agent the image with UUID %s : %s' % (imageUUID, result[1])) ret = False else: image_path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], imageUUID) shortname = self.getClientShortname(computerMACAddress) if shortname: self.logger.info('Imaging: Disk image of client %s (%s) created successfully into %s' % (shortname, computerMACAddress, image_path)) else: self.logger.info('Imaging: Disk image of unknown client (%s) created successfully into %s' % (computerMACAddress, image_path)) ret = True return ret def _gotMAC(result): """ Process result return by getComputerByMac, BTW should be either False or the required info """ if not result: self.logger.error("Can't get computer UUID for MAC address %s" % (computerMACAddress)) return False else: # start gathering details about our image path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], imageUUID) image = Pulse2Image(path) if not image: state = "INVALID" size = 0 # FIXME : use actual size name = "Failed backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) elif image.has_error : state = "FAILED" size = image.size name = "Backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) else : state = "DONE" size = image.size name = "Backup of %s" % result['shortname'] desc = "%s, %s" % (rfc3339Time(), humanReadable(size)) c_uuid = result['uuid'] updateDate = tuple(gmtime()) client = self._getXMLRPCClient() func = 'imaging.imageUpdate' # size converted to float to bypass xmlrpc limitations args = (self.config.imaging_api['uuid'], c_uuid, imageUUID, name, desc, float(size), updateDate, state) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, RPCReplay().onError, errbackArgs = (func, args, False)) return True if not isUUID(imageUUID): self.logger.error("Bad image UUID %s" % str(imageUUID)) ret = False elif not isMACAddress(computerMACAddress): self.logger.error("Bad computer MAC %s" % str(computerMACAddress)) ret = False else: d = self.getComputerByMac(computerMACAddress) d.addCallback(_gotMAC) return d return ret
if result[0]: self.myUUIDCache.set(result[1]['uuid'], MACAddress, result[1]['shortname'].encode('utf-8'), result[1]['fqdn'].encode('utf-8'), result[1]['entity'].encode('utf-8') ) self.logger.debug('Imaging: Updating cache for %s' % (MACAddress)) return result[1] else: self.logger.debug("Imaging: Unable to resolve %s neither from cache nor from database (unknown computer?)" % (MACAddress)) return False except Exception, e: self.logger.error('Imaging: While processing result %s for %s : %s' % (result, MACAddress, e)) if not isMACAddress(MACAddress): raise TypeError('Bad MAC address: %s' % MACAddress) # try to extract from our cache res = self.myUUIDCache.getByMac(MACAddress) if res: # fetched from cache res['shortname'] = res['shortname'].decode('utf-8') res['fqdn'] = res['fqdn'].decode('utf-8') res['entity'] = res['entity'].decode('utf-8') return maybeDeferred(lambda x: x, res) else: # cache fetching failed, try to obtain the real value self.logger.debug('Imaging: Unable to resolve %s from cache, querying database' % (MACAddress)) client = self._getXMLRPCClient() func = 'imaging.getComputerByMac' args = [MACAddress] d = client.callRemote(func, *args)
def computerCreateImageDirectory(self, mac): """ Create an image folder for client <mac> This is quiet different as for the LRS, where the UUID was given in revosavedir (kernel command line) : folder is now generated real-time, if no generation has been done backup is dropped client-side @param mac : The mac address of the client @type mac: MAC Address @return: True if the folder @rtype: bool """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error('Imaging: Couldn\'t register on the MMC agent the image with UUID %s : %s' % (image_uuid, str(result))) ret = False elif not result[0]: self.logger.error('Imaging: Couldn\'t register on the MMC agent the image with UUID %s : %s' % (image_uuid, result[1])) ret = False else: image_path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], image_uuid) self.logger.debug('Imaging: Successfully registered disk image to %s' % image_path) ret = image_uuid return ret def _gotMAC(result): """ Process result return by getComputerByMac, BTW should be either False or the required info """ if not result: self.logger.error("Can't get computer UUID for MAC address %s" % (mac)) os.rmdir(os.path.join(target_folder, image_uuid)) return False else: # start gathering details about our image c_uuid = result['uuid'] isMaster = False # by default, an image is private path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], image_uuid) size = 0 creationDate = tuple(gmtime()) name = "Backup of %s" % result['shortname'] desc = "In Progress" creator = "" state = "EMPTY" # FIXME : define and use consts client = self._getXMLRPCClient() func = 'imaging.imageRegister' args = (self.config.imaging_api['uuid'], c_uuid, image_uuid, isMaster, name, desc, path, size, creationDate, creator, state) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, RPCReplay().onError, errbackArgs = (func, args, False)) return image_uuid shortname = self.getClientShortname(mac) if shortname: self.logger.info('Imaging: Client %s (%s) is creating a disk image' % (shortname, mac)) else: self.logger.info('Imaging: Client %s is creating a disk image' % (mac)) if not isMACAddress(mac): raise TypeError target_folder = os.path.join(PackageServerConfig().imaging_api['base_folder'], PackageServerConfig().imaging_api['masters_folder']) # compute our future UUID, using the MAC address as node # according the doc, the node is a 48 bits (= 6 bytes) integer attempts = 10 while attempts: image_uuid = str(uuid.uuid1(macToNode(mac))) if not os.path.exists(os.path.join(target_folder, image_uuid)): break attempts -= 1 if not attempts: self.logger.warn('Imaging: I was not able to create a folder for client %s' % (mac)) return maybeDeferred(lambda x: x, False) try: os.mkdir(os.path.join(target_folder, image_uuid)) except Exception, e: self.logger.warn('Imaging: I was not able to create folder %s for client %s : %s' % (os.path.join(target_folder, image_uuid), mac, e)) return maybeDeferred(lambda x: x, False)
def logClientAction(self, mac, level, phase, message): """ Remote logging. Mainly used to send progress info to our mmc-agent. @param mac : The mac address of the client @param level : the log level @param phase : what the client was doing when the info was logged @param message : the log message itself @raise TypeError: if MACAddress is malformed @return: a deferred object resulting to 1 if processing was successful, else 0. @rtype: int """ def _infoLogs(message): """ Display Some info logs: * When a machine is booting * When a restoration is done """ shortname = self.getClientShortname(mac) if 'booted' in message: if shortname: self.logger.info('Imaging: Client %s (%s) has booted' % (shortname, mac)) else: self.logger.info('Imaging: Unknown client (%s) has booted' % (mac)) elif 'restoration started' in message: # image UUID is in the 36 last characters of message variable # message[-37:-1] to get image UUID if shortname: self.logger.info('Imaging: Client %s (%s) is restoring a disk image (%s)' % (shortname, mac, message[-37:-1])) else: self.logger.info('Imaging: Unknown client (%s) is restoring a disk image (%s)' % (mac, message[-37:-1])) elif 'restoration finished' in message: # image UUID is in the 36 last characters of message variable # message[-37:-1] to get image UUID if shortname: self.logger.info('Imaging: Disk image (%s) restored successfully to client %s (%s)' % (message[-37:-1], shortname, mac)) else: self.logger.info('Imaging: Disk image (%s) restored successfully to unknown client (%s)' % (message[-37:-1], mac)) def _getmacCB(result): displayed_statuses = ['booted', 'restoration started', 'restoration finished'] if result and type(result) == dict : if any(s in message for s in displayed_statuses): _infoLogs(message) # Display some info logs client = self._getXMLRPCClient() func = 'imaging.logClientAction' args = (self.config.imaging_api['uuid'], result['uuid'], level, phase, message) d = client.callRemote(func, *args) d.addCallbacks(lambda x : True, RPCReplay().onError, errbackArgs = (func, args, 0)) return d if any(s in message for s in displayed_statuses): _infoLogs(message) # Display some info logs return False if not isMACAddress(mac): raise TypeError self.logger.debug('Imaging: Client %s sent a log message while %s (%s) : %s' % (mac, phase, level, message)) d = self.getComputerByMac(mac) d.addCallback(_getmacCB) return d
def test_MACNotValid(self): self.assertFalse(isMACAddress('00:11:aa:BB:22:zz')) self.assertFalse(isMACAddress('00:11:aa:BB:22:33:00'))
def computerCreateImageDirectory(self, mac): """ Create an image folder for client <mac> This is quiet different as for the LRS, where the UUID was given in revosavedir (kernel command line) : folder is now generated real-time, if no generation has been done backup is dropped client-side @param mac : The mac address of the client @type mac: MAC Address @return: True if the folder @rtype: bool """ def _onSuccess(result): if type(result) != list and len(result) != 2: self.logger.error( 'Imaging: Couldn\'t register on the MMC agent the image with UUID %s : %s' % (image_uuid, str(result))) ret = False elif not result[0]: self.logger.error( 'Imaging: Couldn\'t register on the MMC agent the image with UUID %s : %s' % (image_uuid, result[1])) ret = False else: image_path = os.path.join( self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], image_uuid) self.logger.debug( 'Imaging: Successfully registered disk image to %s' % image_path) ret = image_uuid return ret def _gotMAC(result): """ Process result return by getComputerByMac, BTW should be either False or the required info """ if not result: self.logger.error( "Can't get computer UUID for MAC address %s" % (mac)) os.rmdir(os.path.join(target_folder, image_uuid)) return False else: # start gathering details about our image c_uuid = result['uuid'] isMaster = False # by default, an image is private path = os.path.join(self.config.imaging_api['base_folder'], self.config.imaging_api['masters_folder'], image_uuid) size = 0 creationDate = tuple(gmtime()) name = "Backup of %s" % result['shortname'] desc = "In Progress" creator = "" state = "EMPTY" # FIXME : define and use consts client = self._getXMLRPCClient() func = 'imaging.imageRegister' args = (self.config.imaging_api['uuid'], c_uuid, image_uuid, isMaster, name, desc, path, size, creationDate, creator, state) d = client.callRemote(func, *args) d.addCallbacks(_onSuccess, RPCReplay().onError, errbackArgs=(func, args, False)) return image_uuid shortname = self.getClientShortname(mac) if shortname: self.logger.info( 'Imaging: Client %s (%s) is creating a disk image' % (shortname, mac)) else: self.logger.info('Imaging: Client %s is creating a disk image' % (mac)) if not isMACAddress(mac): raise TypeError target_folder = os.path.join( PackageServerConfig().imaging_api['base_folder'], PackageServerConfig().imaging_api['masters_folder']) # compute our future UUID, using the MAC address as node # according the doc, the node is a 48 bits (= 6 bytes) integer attempts = 10 while attempts: image_uuid = str(uuid.uuid1(macToNode(mac))) if not os.path.exists(os.path.join(target_folder, image_uuid)): break attempts -= 1 if not attempts: self.logger.warn( 'Imaging: I was not able to create a folder for client %s' % (mac)) return maybeDeferred(lambda x: x, False) try: os.mkdir(os.path.join(target_folder, image_uuid)) except Exception, e: self.logger.warn( 'Imaging: I was not able to create folder %s for client %s : %s' % (os.path.join(target_folder, image_uuid), mac, e)) return maybeDeferred(lambda x: x, False)