Source code for pyfarm.agent.sysinfo.software

# No shebang line, this module is meant to be imported
#
# Copyright 2015 Ambient Entertainment GmbH & Co. KG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Software
--------

Contains utilities to check the availability of certain software on the local
machine.
"""

try:
    from httplib import OK, INTERNAL_SERVER_ERROR, NOT_FOUND
except ImportError:  # pragma: no cover
    from http.client import OK, INTERNAL_SERVER_ERROR, NOT_FOUND

import imp, sys

import treq

from twisted.internet import reactor, threads
from twisted.internet.defer import inlineCallbacks, returnValue, Deferred

from pyfarm.agent.logger import getLogger
from pyfarm.agent.config import config
from pyfarm.agent.http.core.client import get_direct, http_retry_delay

logger = getLogger("agent.sysinfo.software")


[docs]class VersionNotFound(Exception): pass
[docs]@inlineCallbacks def get_software_version_data(software, version): """ Asynchronously fetches the known data about the given software version from the master. :param str software: The name of the software to get data for :param str version: The name of the version to get data for :return: Returns information about the given software version from the master """ url = "{master_api}/software/{software}/versions/{version}".\ format(master_api=config.get("master_api"), software=software, version=version) while True: try: response = yield get_direct(url) except Exception as error: delay = http_retry_delay() logger.error( "Failed to get data about software %s, version %s: %r. Will " "retry in %s seconds.", software, version, error, delay) deferred = Deferred() reactor.callLater(delay, deferred.callback, None) yield deferred else: if response.code == OK: data = yield treq.json_content(response) returnValue(data) elif response.code >= INTERNAL_SERVER_ERROR: delay = http_retry_delay() logger.warning( "Could not get data for software %s, version %s, server " "responded with INTERNAL_SERVER_ERROR. Retrying in %s " "seconds.", software, version, delay) deferred = Deferred() reactor.callLater(delay, deferred.callback, None) yield deferred elif response.code == NOT_FOUND: logger.error("Got 404 NOT FOUND from server on getting data " "for software %s, version %s", software, version) raise VersionNotFound("This software version was not found or " "has no discovery code.") else: logger.error( "Failed to get data for software %s, version %s: " "Unexpected status from server %s", software, version, response.code) raise Exception("Unknown return code from master: %s" % response.code)
[docs]@inlineCallbacks def get_discovery_code(software, version): """ Asynchronously fetches the discovery code for the given software version from the master. :param str software: The name of the software to get the discovery code for :param str version: The name of the version to get the discovery code for :return: Returns the discovery code from the master/ """ url = "{master_api}/software/{software}/versions/{version}/discovery_code".\ format(master_api=config.get("master_api"), software=software, version=version) while True: try: response = yield get_direct(url) except Exception as error: delay = http_retry_delay() logger.error( "Failed to get discovery code for software %s, version %s: %r. " " Will retry in %s seconds.", software, version, error, delay) deferred = Deferred() reactor.callLater(delay, deferred.callback, None) yield deferred else: if response.code == OK: data = yield treq.content(response) returnValue(data) elif response.code >= INTERNAL_SERVER_ERROR: delay = http_retry_delay() logger.warning( "Could not get discovery code for software %s, version %s, " "server responded with INTERNAL_SERVER_ERROR. Retrying in " "%s seconds.", software, version, delay) deferred = Deferred() reactor.callLater(delay, deferred.callback, None) yield deferred elif response.code == NOT_FOUND: logger.error("Got 404 NOT FOUND from server on getting " "discovery code for software %s, version %s", software, version) raise VersionNotFound("This software version was not found or " "has no discovery code.") else: logger.error( "Failed to get discovery code for software %s, version %s: " "Unexpected status from server %s", software, version, response.code) raise Exception("Unknown return code from master: %s" % response.code)
[docs]@inlineCallbacks def check_software_availability(software, version): """ Asynchronously checks for the availability of a given software in a given version. Will pass True to its callback function if the software could be found, False otherwise. Works only for software versions that have a discovery registered on the master. :param str software: The name of the software to check for :param str version: The name of the version to check for """ module_name = \ "pyfarm.agent.sysinfo.software.{software}_{version}".format( software=software, version=version) module = sys.modules.get(module_name) if module is None: module = imp.new_module(module_name) sys.modules[module_name] = module # Retrieve information about the software/version combination, # update the module with the discovery code and then execute # the discovery function. version_data = yield get_software_version_data(software, version) discovery_code = yield get_discovery_code(software, version) exec discovery_code in module.__dict__ result = yield threads.deferToThread( getattr(module, version_data["discovery_function_name"])) returnValue(result)