def agentFunctionReboot(functionParameterValues):
    # Put a file to indicate what is happening and start the reboot process
    with open(currentFunctionDir + '/doingReboot.json', 'w') as outfile:
        json.dump({'doingRebootAsPartOfFunction': True}, outfile, indent=4)
    comDavra.logInfo('Function: Reboot Device, starting')
    comDavra.runCommandWithTimeout('sudo reboot -h now',
                                   comDavra.conf["scriptMaxTime"])
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 agentFunctionReportAgentConfig(functionParameterValues):
    comDavra.logInfo('Function: Reporting the agent config to server')
    comDavra.reportDeviceConfigurationToServer()
    comDavra.upsertJsonEntry(currentFunctionJson, 'response', comDavra.conf)
    comDavra.upsertJsonEntry(currentFunctionJson, 'status', 'completed')
    checkFunctionFinished()
    return
def sendHeartbeatMetricsToServer():
    dataToSend = [{
        "UUID": comDavra.conf['UUID'],
        "name": "uptime",
        "value": comDavra.getUptimeProcess(),
        "msg_type": "datum"
    }, {
        "UUID": comDavra.conf['UUID'],
        "name": "cpu",
        "value": comDavra.getUptime()[1],
        "msg_type": "datum",
    }, {
        "UUID": comDavra.conf['UUID'],
        "name": "ram",
        "value": comDavra.getRam()[1],
        "msg_type": "datum",
    }, {
        "UUID": comDavra.conf['UUID'],
        "name": "davra.agent.heartbeat",
        "value": {
            "davraAgentVersion": comDavra.davraAgentVersion,
            "heartbeatInterval": comDavra.conf['heartbeatInterval']
        },
        "msg_type": "event"
    }]
    comDavra.logInfo('Sending heartbeat data to: ' + comDavra.conf['server'] +
                     ": " + comDavra.conf['UUID'])
    #print(json.dumps(dataToSend, indent=4))
    statusCode = comDavra.sendDataToServer(dataToSend).status_code
    comDavra.log('Response after sending heartbeat data: ' + str(statusCode))
    return
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 agentFunctionPushAppSnap(functionParameterValues):
    comDavra.logInfo(
        'Function: Pushing Application onto device to run as a snap ' +
        str(functionParameterValues))
    if (functionParameterValues["Snap File From Repo"]):
        comDavra.log('URL is ' + functionParameterValues["File URL"])
    # TODO
    return
def agentFunctionUpdateAgentConfig(functionParameterValues):
    comDavra.logInfo('Function: Updating the agent config to server ' +
                     str(functionParameterValues))
    comDavra.upsertConfigurationItem(functionParameterValues["key"],
                                     functionParameterValues["value"])
    comDavra.reportDeviceConfigurationToServer()
    comDavra.upsertJsonEntry(currentFunctionJson, 'response', comDavra.conf)
    comDavra.upsertJsonEntry(currentFunctionJson, 'status', 'completed')
    checkFunctionFinished()
    return
def reportAgentStarted():
    eventToSend = {
        "UUID": comDavra.conf['UUID'],
        "name": "davra.agent.started",
        "msg_type": "event",
        "value": comDavra.conf
    }
    r = comDavra.sendDataToServer(eventToSend)
    if (r.status_code == 200):
        comDavra.logInfo("Sent event to server to indicate agent started")
    # Update the device labels to reflect this agent version
    comDavra.logInfo("Running davraAgentVersion:" + comDavra.davraAgentVersion)
    comDavra.updateDeviceLabelOnServer("davraAgentVersion",
                                       comDavra.davraAgentVersion)
    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
import time, requests, os.path
from requests.auth import HTTPBasicAuth
import json
from pprint import pprint
import datetime
import paho.mqtt.client as mqtt
import davra_lib as comDavra
# If you add new libraries to the agent, update requirements.txt

# sys.dont_write_bytecode = True
# Confirm configuration available from within the config file
if ('server' not in comDavra.conf or 'UUID' not in comDavra.conf):
    print("Configuration incomplete. Please run setup.py first.")
    sys.exit(1)

comDavra.logInfo('Starting Davra Device Agent.')
comDavra.logInfo('Server: ' + comDavra.conf['server'] + ". Device: " +
                 comDavra.conf['UUID'])

# The job details of currently running job for this device.
# Files are used to track the currently running Job, script and function.
# Each get a dirctory to themselves and a json for tracking.
# The directory "currentJob" etc may be removed when job is finished.
currentJobDir = comDavra.installationDir + '/currentJob'
currentJobJson = currentJobDir + '/job.json'
currentFunctionDir = comDavra.installationDir + "/currentFunction"
currentFunctionJson = currentFunctionDir + "/currentFunction.json"
# Some flags to cache in RAM whether a function/script/job is running
# rather than checking the hard drive continuously
flagIsFunctionRunning = False
flagIsScriptRunning = False