def runFunction(functionName, functionParameterValues): # Always assign a uuid to a function if not already if (functionParameterValues.has_key("functionUuid") == False): functionParameterValues["functionUuid"] = comDavra.generateUuid() # Put a file to indicate what is happening and start the function comDavra.provideFreshDirectory(currentFunctionDir) functionInfo = { 'functionName': functionName, \ 'functionParameterValues': functionParameterValues, \ 'status': 'running', \ 'startTime': comDavra.getMilliSecondsSinceEpoch() } with open(currentFunctionDir + '/currentFunction.json', 'w') as outfile: json.dump(functionInfo, outfile, indent=4) # Only run functions which are within capabilities if (comDavra.conf["capabilities"].has_key(functionName) is False): comDavra.logError( 'Error: Attemping to to run a function which is not in capabilities: ' + functionName) comDavra.logError('Error: Capabilities: ' + str(comDavra.conf["capabilities"])) comDavra.upsertJsonEntry(currentFunctionJson, 'status', 'failed') return # comDavra.log('Running a function:' + json.dumps(functionInfo)) flagIsFunctionRunning = True # # Is this capability something the agent knows how to do if (functionName in agentCapabilityFunctions): comDavra.log('Will run this function within the agent') agentCapabilityFunctions[functionName](functionParameterValues) else: # Send it onwards to the apps who may be able to do it comDavra.log( 'Will run this function within an app rather than the agent') sendMessageFromAgentToApps(functionInfo) return
def sendIotDataToServer(msgFromMqtt): comDavra.log('Sending iotdata to server ') print(str(msgFromMqtt)) dataFromAgent = json.loads(msgFromMqtt["sendIotData"]) if (type(dataFromAgent) == type({})): dataFromAgent = [dataFromAgent] dataForServer = [] for metric in dataFromAgent: if (metric.has_key("UUID") == False): metric["UUID"] = comDavra.conf["UUID"] if ("timestamp" not in metric): metric["timestamp"] = comDavra.getMilliSecondsSinceEpoch() if ("name" in metric and "value" in metric and "msg_type" in metric): comDavra.log('Sending data now to server ') print(str(metric)) dataForServer.append(metric) else: comDavra.logError( 'Not sending data to server as it appears incomplete: ' + str(metric)) if dataForServer: comDavra.log('Sending data to Server: ' + str(dataForServer)) statusCode = comDavra.sendDataToServer(dataForServer).status_code comDavra.log('Response after sending iotdata to server: ' + str(statusCode))
def agentFunctionRunScriptBash(functionParameterValues): if "script" not in functionParameterValues: comDavra.upsertJsonEntry(currentFunctionJson, 'response', 'script missing') comDavra.upsertJsonEntry(currentFunctionJson, 'status', 'failed') comDavra.logError( 'Could not run script as function because no script to run') checkFunctionFinished() return # Put the script into the function dir scriptFile = open(currentFunctionDir + "/script.sh", "a") scriptFile.write(str(functionParameterValues["script"])) scriptFile.close() comDavra.logInfo('Running script ' + str(functionParameterValues["script"])) os.system("chmod 777 " + currentFunctionDir + "/script.sh") time.sleep(0.5) # Time for file flush to disk # Run the script with -x flag so it prints each command before ruuning it. # This allows the UI to show it formatted better for user on jobs page scriptResponse = comDavra.runCommandWithTimeout('cd ' + currentFunctionDir + \ ' && sudo bash -x ' + currentFunctionDir + "/script.sh", comDavra.conf["scriptMaxTime"]) # scriptResponse is a tuple of (exitStatusCode, stdout) . For exitStatusCode: 0 = success, 1 = failed ) comDavra.log("Script response: " + str(scriptResponse[1])) scriptStatus = 'completed' if (scriptResponse[0] == 0) else 'failed' comDavra.upsertJsonEntry(currentFunctionJson, 'response', str(scriptResponse[1])) comDavra.upsertJsonEntry(currentFunctionJson, 'status', scriptStatus) checkFunctionFinished()
def runDavraJob(jobObject): # Catch situation where a job is already running if (os.path.isfile(currentJobJson) == True): comDavra.logWarning( 'A job is already running. Will not start another job. ' + jobObject["UUID"]) return comDavra.log('Start Run of job ' + jobObject["UUID"]) try: flagIsJobRunning = True # Write this job to disk as the current job jobObject['devices'][0][ 'startTime'] = comDavra.getMilliSecondsSinceEpoch() jobObject['devices'][0]['status'] = 'running' comDavra.provideFreshDirectory(currentJobDir) with open(currentJobJson, 'w') as outfile: json.dump(jobObject, outfile, indent=4) if (jobObject.has_key('jobConfig') and jobObject['jobConfig']['type'].lower() == 'runfunction'): comDavra.logInfo('Job Run: type is runFunction. ' + jobObject['jobConfig']['functionName']) runFunction(jobObject['jobConfig']['functionName'], jobObject['jobConfig']['functionParameterValues']) return # Reaching here means the job type was not recognised so that is a failed situation updateJobWithResult('failed', 'Unknown job type') checkCurrentJob() return except Exception as e: # Reaching here means the job type was not recognised so that is a failed situation comDavra.logError('Job Error ' + str(e)) updateJobWithResult('failed', 'Unknown job type') checkCurrentJob() return
def mqttOnMessageDevice(client, userdata, msg): payload = str(msg.payload) if (comDavra.isJson(payload)): processMessageFromAppToAgent(json.loads(payload)) else: comDavra.logError( 'ERROR: Mqtt Device Broker: Received NON json Mqtt message: ' + payload) return
def checkFunctionFinished(): currentFunctionInfo = None if (os.path.isfile(currentFunctionJson) == True): with open(currentFunctionJson) as data_file: currentFunctionInfo = json.load(data_file) if (currentFunctionInfo.has_key("status")): if (currentFunctionInfo["status"] == 'completed' or currentFunctionInfo["status"] == 'failed'): # Function has finished, report back if part of a currently running job functionResponse = currentFunctionInfo[ "response"] if currentFunctionInfo.has_key( "response") else "" reportFunctionFinishedAsEventToServer(currentFunctionInfo) updateJobWithResult(currentFunctionInfo["status"], functionResponse) comDavra.provideFreshDirectory( currentFunctionDir ) # Wipe the currently running function files flagIsFunctionRunning = False comDavra.log('Function finished ' + json.dumps(currentFunctionInfo)) else: # Has the function been running for too long. If so, declare it as failed if (currentFunctionInfo.has_key("startTime") is True): if(comDavra.getMilliSecondsSinceEpoch() - int(currentFunctionInfo["startTime"]) \ > int(comDavra.conf["scriptMaxTime"]) * 1000): comDavra.logWarning( 'Function has been running for too long - declare it failed' ) currentFunctionInfo["status"] = "failed" currentFunctionInfo[ "endTime"] = comDavra.getMilliSecondsSinceEpoch() with open(currentFunctionDir + '/currentFunction.json', 'w') as outfile: json.dump(currentFunctionInfo, outfile, indent=4) reportFunctionFinishedAsEventToServer( currentFunctionInfo) flagIsFunctionRunning = False comDavra.logWarning( 'Function finished due to timeout ' + json.dumps(currentFunctionInfo)) # If a function has finished, it may have been part of a job. Check if that is finished checkCurrentJob() return else: comDavra.logError( 'Error situation. Function has no status. Should not occur.') return
def agentFunctionPushAppWithInstaller(functionParameterValues): comDavra.logInfo( 'Function: Pushing Application onto device to run as a service ' + str(functionParameterValues)) if (functionParameterValues["Installation File"]): installationFile = functionParameterValues["Installation File"] # Download the app tarball try: tmpPath = '/tmp/' + str(comDavra.getMilliSecondsSinceEpoch()) comDavra.ensureDirectoryExists(tmpPath) comDavra.runCommandWithTimeout( 'cd ' + tmpPath + ' && /usr/bin/curl -LO ' + installationFile, 300) comDavra.runCommandWithTimeout( 'cd ' + tmpPath + ' && /bin/tar -xvf ./* ', 300) comDavra.runCommandWithTimeout( 'cd ' + tmpPath + ' && chmod 777 ./* ', 300) # Ensure the install.sh is unix format comDavra.runCommandWithTimeout( "cd " + tmpPath + " && sed -i $'s/\r$//' install.sh ", 30) installedAppPath = comDavra.installationDir + '/apps/' + str( comDavra.getMilliSecondsSinceEpoch()) comDavra.ensureDirectoryExists(installedAppPath) comDavra.runCommandWithTimeout( 'cd ' + tmpPath + ' && cp -r * ' + installedAppPath, 300) installResponse = comDavra.runCommandWithTimeout( 'cd ' + installedAppPath + ' && bash ./install.sh ', comDavra.conf["scriptMaxTime"]) comDavra.log('Installation response: ' + str(installResponse[1])) scriptStatus = 'completed' if (installResponse[0] == 0) else 'failed' comDavra.upsertJsonEntry(currentFunctionJson, 'response', str(installResponse[1])) comDavra.upsertJsonEntry(currentFunctionJson, 'status', scriptStatus) except Exception as e: comDavra.logError('Failed to download application:' + installationFile + " : Error: " + str(e)) comDavra.upsertJsonEntry(currentFunctionJson, 'status', 'failed') checkFunctionFinished() comDavra.log('Finished agentFunctionPushAppWithInstaller') checkFunctionFinished() else: comDavra.logWarning('Action parameters missing, nothing to do') # TODO return
def reportJobStatus(): comDavra.log('Current job is finished so reporting it to server now') jobObject = None with open(currentJobJson) as data_file: jobObject = json.load(data_file) deviceJobObject = jobObject['devices'][0] headers = comDavra.getHeadersForRequests() apiEndPoint = comDavra.conf['server'] + '/api/v1/jobs/' + jobObject[ 'UUID'] + '/' + deviceJobObject['UUID'] comDavra.logInfo('Reporting job update to server: ' + apiEndPoint + ' : ' + json.dumps(deviceJobObject)) r = comDavra.httpPut(apiEndPoint, deviceJobObject) if (r.status_code == 200): comDavra.log("Updated server after running job.") # The job has completed and been reflected at the server so delete the currentJobJson file os.remove(currentJobJson) else: comDavra.log("Issue while updating server after running job. " + str(r.status_code)) comDavra.log(r.content) # Report job event to server as an iotdata event eventToSend = { "UUID": deviceJobObject['UUID'], "name": "davra.job.finished", "msg_type": "event", "value": deviceJobObject, "tags": { "status": deviceJobObject["status"] } } apiEndPoint = comDavra.conf['server'] + '/api/v1/iotdata' r = comDavra.httpPut(apiEndPoint, eventToSend) if (r.status_code == 200): comDavra.log("Sent event to server after running job.") else: comDavra.logError( "Issue while sending event to server after running job. " + str(r.status_code)) comDavra.logError(r.content) comDavra.provideFreshDirectory( currentJobDir) # Remove the file and dir on disk flagIsJobRunning = False return
def mqttConnectToServer(): if(comDavra.conf.has_key("mqttBrokerServerHost") and len(comDavra.conf["mqttBrokerServerHost"]) > 3) \ and comDavra.conf.has_key("apiToken"): clientOfServer = mqtt.Client() clientOfServer.on_connect = mqttOnConnectServer clientOfServer.on_message = mqttOnMessageServer comDavra.log( 'Starting to connect to MQTT broker running on Davra server ' + comDavra.conf["mqttBrokerServerHost"]) apiTokenForDevice = comDavra.conf["apiToken"] clientOfServer.username_pw_set(username=comDavra.conf["UUID"], password=apiTokenForDevice) try: clientOfServer.connect(comDavra.conf["mqttBrokerServerHost"]) clientOfServer.loop_start( ) # Starts another thread to monitor incoming messages except Exception as e: comDavra.logError('Experienced error connecting to mqtt at ' + comDavra.conf["mqttBrokerServerHost"] + ":" + str(e)) else: comDavra.logError('No MQTT broker configured for Davra server')
def checkForPendingJob(): headers = comDavra.getHeadersForRequests() dataToSend = { "deviceUUID": comDavra.conf['UUID'], "deviceStatus": "pending", "jobStatus": "active", "oldest": True } r = comDavra.httpPut(comDavra.conf['server'] + '/api/v1/jobs', dataToSend) if (r.status_code == 200): pendingJobs = json.loads(r.content) if comDavra.isJson( r.content) else [] if (pendingJobs != [] and len(pendingJobs) > 0): comDavra.log('Pending job to run: ' + str(pendingJobs[0])) runDavraJob(pendingJobs[0]) else: comDavra.log('No pending job to run.') return else: comDavra.logError("Issue while checking for pending job. " + str(r.status_code)) comDavra.logError(r.content) return (r.status_code)
if(r.status_code == 200): jsonContent = json.loads(r.content) latitude = jsonContent['lat'] longitude = jsonContent['lon'] return (latitude, longitude) else: comDavra.logWarning("Cannot reach GeoIp server. " + str(r.status_code)) return (0,0) (piLatitude, piLongitude) = getLatLong() comDavra.log('Latitude/Longitude estimated as ' + str(piLatitude) + ", " + str(piLongitude)) # Confirm MQTT Broker on agent if(comDavra.checkIsAgentMqttBrokerInstalled() == False): comDavra.logError('MQTT Broker not installed') comDavra.upsertConfigurationItem("mqttBrokerAgentHost", '') else: comDavra.log('MQTT Broker installed and running') comDavra.upsertConfigurationItem("mqttBrokerAgentHost", '127.0.0.1') # To enable advanced security on mqtt requiring usernames for connections #comDavra.upsertConfigurationItem("mqttRestrictions", 'localhost,username') # To enable basic security which is only localhost connections to mqtt comDavra.upsertConfigurationItem("mqttRestrictions", 'localhost') setDeviceMqttBrokerSecurity() # Send an event to the server to inform it of the installation dataToSend = { "UUID": comDavra.conf['UUID'],
clientOfDevice = None if (comDavra.conf.has_key("mqttBrokerAgentHost") and len(comDavra.conf["mqttBrokerAgentHost"]) > 3): clientOfDevice = mqtt.Client() clientOfDevice.on_connect = mqttOnConnectDevice clientOfDevice.on_message = mqttOnMessageDevice clientOfDevice.username_pw_set(username=comDavra.conf["UUID"], password=comDavra.conf["apiToken"]) comDavra.logInfo('Starting to connect to MQTT broker running on device ' + comDavra.conf["mqttBrokerAgentHost"]) try: clientOfDevice.connect(comDavra.conf["mqttBrokerAgentHost"]) clientOfDevice.loop_start( ) # Starts another thread to monitor incoming messages except Exception as e: comDavra.logError('Experienced error connecting to mqtt at ' + comDavra.conf["mqttBrokerServerHost"] + ":" + str(e)) else: comDavra.logError('No MQTT broker configured on device') ########################### Process Messages from Device Application to Device Agent # These messages may arrive by mqtt from app to agent or api calls or flat file comms # msg should be a json object def processMessageFromAppToAgent(msg): # Ignore any messages this agent published if (msg.has_key("fromAgent")): return comDavra.log('processMessageFromAppToAgent: incoming msg: ' + str(msg)) if (msg.has_key("registerCapability")): capabilityName = msg["registerCapability"]