Example #1
0
def setup_mc():
    """Lance un serveur memcached pour les tests."""
    global mc_pid

    # Juste pour être sûr...
    if mc_pid:
        LOGGER.info("Forcefully stopping previous instance of memcached")
        teardown_mc()

    settings['correlator']['memcached_host'] = "127.0.0.1"
    port = get_available_port()
    settings['correlator']['memcached_port'] = port
    env = os.environ.copy()
    env["PATH"] += ":/usr/sbin" # Sur mandriva, memcached est dans /usr/sbin
    LOGGER.info("Configuring memcached to run on port %d", port)
    mc_pid = subprocess.Popen(["memcached", "-l", "127.0.0.1", "-p", str(port)],
                               env=env, close_fds=True).pid
    # Give it time to start up properly. I should try a client connection in a
    # while loop. Oh well...
    MemcachedConnection.reset()
    time.sleep(1)
    MemcachedConnection()
Example #2
0
    def __init__(self, msgid, transaction=True, timeout=None):
        """
        Initialisation d'un contexte de corrélation (au moyen de MemcacheD).

        @param msgid: Identifiant de l'alerte brute
            reçue par le corrélateur.
        @type msgid: C{basestring}.
        """
        self._connection = MemcachedConnection()
        self._transaction = transaction
        self._id = str(msgid)
        if timeout is None:
            timeout = settings['correlator'].as_float('context_timeout')
        self._timeout = timeout
Example #3
0
def teardown_mc():
    """Détruit le serveur memcached créé pour le passage d'un test."""
    global mc_pid
    LOGGER.info("Killing memcached instance running on port %d",
        settings['correlator']['memcached_port'])
    try:
        # Tue le serveur memcached lancé en arrière-plan.
        os.kill(mc_pid, signal.SIGTERM)
        os.wait() # Avoid zombies. Bad zombies.
    except OSError, e:
        # We mostly ignore errors, maybe we should
        # do something more useful here.
        print e
    finally:
        mc_pid = None
    return MemcachedConnection.reset()

with_mc = nose.with_setup(setup_mc, teardown_mc)

#Create an empty database before we start our tests for this module
def setup_db():
    """Crée toutes les tables du modèle dans la BDD."""
    from vigilo.models.tables.grouppath import GroupPath
    from vigilo.models.tables.usersupitem import UserSupItem
    tables = metadata.tables.copy()
    del tables[GroupPath.__tablename__]
    del tables[UserSupItem.__tablename__]
    metadata.create_all(tables=tables.itervalues())
    metadata.create_all(tables=[GroupPath.__table__, UserSupItem.__table__])

#Teardown that database
Example #4
0
class Context(object):
    """
    Un contexte de corrélation pouvant recevoir des attributs arbitraires.

    Un certain nombre d'attributs sont prédéfinis et utilisés par le corrélateur
    pour alimenter la base de données.

    Les attributs prédéfinis sont :
        -   raw_event_id : identifiant de l'événement brut (C{int}).
        -   successors_aggregates : liste des identifiants des agrégats
            qui doivent être fusionnés avec celui de l'événement courant
            (C{list} of C{int}).
        -   predecessors_aggregates : liste des agrégats auxquels doit
            être rattaché l'événement (C{list} of C{int}).
        -   previous_state : état précédent du service (C{int}).
        -   statename : nom de l'état courant du service (C{str}).
        -   servicename : nom du service associé au contexte (C{str}).
        -   hostname : nom de l'hôte qui héberge le service (C{str}).
        -   impacted_hls : liste des identifiants des services de haut
            niveau impactés par l'événement (C{list} of C{int}).
        -   occurrences_count : nombre d'occurrences de l'alerte (C{int}).
        -   priority : priorité de l'événement corrélé (C{int}).
        -   no_alert : empêche la génération d'une alerte corrélée (C{bool}).
        -   payload : message brut (XML sérialisé) de l'événement reçu (C{str}).
        -   idsupitem : identifiant de l'élément supervisé impacté (C{int}).
    """

    def __init__(self, msgid, transaction=True, timeout=None):
        """
        Initialisation d'un contexte de corrélation (au moyen de MemcacheD).

        @param msgid: Identifiant de l'alerte brute
            reçue par le corrélateur.
        @type msgid: C{basestring}.
        """
        self._connection = MemcachedConnection()
        self._transaction = transaction
        self._id = str(msgid)
        if timeout is None:
            timeout = settings['correlator'].as_float('context_timeout')
        self._timeout = timeout

    def get(self, prop):
        """
        Récupération de la valeur d'un des attributs du contexte.

        @param prop: Nom de l'attribut, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        @return: Valeur de l'attribut demandé.
        @rtype: C{mixed}
        """
        # Les attributs pour lesquels il y a un getter
        # sont retournés en utilisant le getter.
        if prop in ('topology', 'last_topology_update'):
            return object.__getattribute__(self, prop)

        key = 'vigilo:%s:%s' % (prop, self._id)
        return self._connection.get(key, self._transaction)

    def set(self, prop, value, timeout=NoTimeoutOverride):
        """
        Modification dynamique d'un des attributs du contexte.

        @param prop: Nom de l'attribut, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        @param value: Valeur à donner à l'attribut. La valeur doit
            être sérialisable à l'aide du module C{pickle} de Python.
        @type value: C{mixed}
        @param timeout: Durée de rétention (en secondes) de la donnée.
            Si omis, la durée de rétention globale associée au contexte
            est utilisée.
        @type timeout: C{float}
        """
        key = 'vigilo:%s:%s' % (prop, self._id)
        if timeout is NoTimeoutOverride:
            timeout = self._timeout
        return self._connection.set(
            key,
            value,
            self._transaction,
            time=timeout)

    def delete(self, prop):
        """
        Suppression dynamique d'un attribut du contexte.

        @param prop: Nom de l'attribut, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        """
        key = 'vigilo:%s:%s' % (prop, self._id)
        return self._connection.delete(key, self._transaction)

    def getShared(self, prop):
        """
        Récupération de la valeur d'un des attributs partagés du contexte.

        @param prop: Nom de l'attribut partagé, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        @return: Valeur de l'attribut partagé demandé.
        @rtype: C{mixed}
        """
        key = 'shared:%s' % prop
        return self._connection.get(key, self._transaction)

    def setShared(self, prop, value, timeout=NoTimeoutOverride):
        """
        Modification dynamique d'un des attributs partagés du contexte.

        @param prop: Nom de l'attribut partagé, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        @param value: Valeur à donner à l'attribut partagé. La valeur doit
            être sérialisable à l'aide du module C{pickle} de Python.
        @type value: C{mixed}
        @param timeout: Durée de rétention (en secondes) de la donnée.
            Si omis, la durée de rétention globale associée au contexte
            est utilisée.
        @type timeout: C{float}
        """
        key = 'shared:%s' % prop
        if timeout is NoTimeoutOverride:
            timeout = self._timeout
        return self._connection.set(
            key,
            value,
            self._transaction,
            time=timeout)

    def deleteShared(self, prop):
        """
        Suppression dynamique d'un attribut partagé du contexte.

        @param prop: Nom de l'attribut partagé, tout identifiant Python valide
            est autorisé, sauf ceux dont le nom commence par '_'.
        @type prop: C{str}
        """
        key = 'shared:%s' % prop
        return self._connection.delete(key, self._transaction)
 def setUp(self):
     super(TestMemcachedConnection, self).setUp()
     helpers.setup_db()
     helpers.setup_mc()
     self.cache = MemcachedConnection()
     return defer.succeed(None)
class TestMemcachedConnection(unittest.TestCase):
    """
    Test des méthodes de la classe 'MemcachedConnection'

    Le setUp et le tearDown sont décorés par @deferred() pour que la création
    de la base soit réalisée dans le même threads que les accès dans les tests.
    """

    @deferred(timeout=60)
    def setUp(self):
        super(TestMemcachedConnection, self).setUp()
        helpers.setup_db()
        helpers.setup_mc()
        self.cache = MemcachedConnection()
        return defer.succeed(None)

    @deferred(timeout=60)
    def tearDown(self):
        """Arrêt du serveur Memcached à la fin de chaque test."""
        super(TestMemcachedConnection, self).tearDown()
        self.cache = None
        helpers.teardown_mc()
        helpers.teardown_db()
        return defer.succeed(None)

    @deferred(timeout=10)
    def test_singleton(self):
        """Unicité de la connexion au serveur MemcacheD."""
        # On instancie une 2ème fois la classe MemcachedConnection.
        conn = MemcachedConnection()

        # On s'assure que les deux instances
        # représentent en fait le même objet.
        self.assertEqual(conn, self.cache)
        return defer.succeed(None)

    def _connect(self):
        host = helpers.settings['correlator']['memcached_host']
        port = helpers.settings['correlator'].as_int('memcached_port')
        d_connection = protocol.ClientCreator(
                reactor, MemCacheProtocol
            ).connectTCP(host, port)
        return d_connection

    @deferred(timeout=60)
    @defer.inlineCallbacks
    def test_set(self):
        """Association d'une valeur à une clé"""
        # On initialise le nom de la clé et de la valeur associée
        key = "vigilo_test_set"
        value = "test_set"

        set_value = yield self.cache.set(key, value)
        LOGGER.info("'%s' set to '%s'", key, set_value)

        # On vérifie que la clé a bien été ajoutée
        # et qu'elle est bien associée à la valeur 'value'.
        connection = yield self._connect()
        LOGGER.info("Connected using %r", connection)
        received = yield connection.get(key)
        LOGGER.info("Received: %r", received)
        self.assertEqual(pickle.loads(received[-1]), value)

    @deferred(timeout=60)
    @defer.inlineCallbacks
    def test_get(self):
        """Récupération de la valeur associée à une clé"""

        # On initialise le nom de la clé et de la valeur associée
        key = "vigilo_test_get"
        value = "test_get"

        # On associe la valeur 'value' à la clé 'key'.
        connection = yield self._connect()
        LOGGER.info("Connected using %r", connection)
        res = yield connection.set(key, pickle.dumps(value))
        LOGGER.info("Success? %r", res)

        # On tente à nouveau de récupérer la valeur associée à la clé 'key'
        result = yield self.cache.get(key)

        # On vérifie que la méthode get retourne bien 'value'.
        self.assertEqual(result, value)

    @deferred(timeout=60)
    @defer.inlineCallbacks
    def test_delete(self):
        """Suppression d'une clé"""

        # On initialise le nom de la clé et de la valeur associée
        key = "vigilo_test_delete"
        value = "test_delete"

        # On ajoute la clé 'key'.
        connection = yield self._connect()
        LOGGER.info("Connected using %r", connection)
        yield connection.set(key, value)

        # On tente à nouveau de supprimer la clé 'key'
        yield self.cache.delete(key)

        # On s'assure que la clé a bien été supprimée
        value = yield connection.get(key)
        self.assertEquals(None, value[-1])

    @deferred(timeout=60)
    @defer.inlineCallbacks
    def test_key_spaces(self):
        """Gestion des espaces dans une clé"""
        # On initialise le nom de la clé et de la valeur associée
        key = "vigilo test espace"
        value = "test_espace"

        set_value = yield self.cache.set(key, value)
        LOGGER.info("'%s' set to '%s'", key, set_value)
        get_value = yield self.cache.get(key, value)
        LOGGER.info("'%s' value is '%s'", key, get_value)
        yield self.cache.delete(key)
        LOGGER.info("'%s' deleted", key)

    @deferred(timeout=60)
    @defer.inlineCallbacks
    def test_reconnection(self):
        """Reconnexion automatique à memcached"""
        yield self.cache.set("test", 42)

        # On coupe la connexion. Comme la factory n'a pas demandé
        # cette coupure, elle va automatiquement tenter une reconnexion.
        self.cache._cache._instance.transport.loseConnection()

        # Ce deferred va émettre une exception car la connexion
        # a été perdue entre temps et car le deferred avait été
        # créé AVANT que la perte de connexion ne soit détectée.
        # Avant Twisted 9.0, l'exception est une defer.TimoutError,
        # ensuite il s'agit d'une error.ConnectionDone
        try:
            yield self.cache.get("test")
            self.fail("A TimeoutError exception was expected, got nothing.")
        except (defer.TimeoutError, error.ConnectionDone, KeyboardInterrupt):
            pass
        except Exception, e:
            self.fail("A TimeoutError exception was expected, got %r" % e)

        # Ce get() fonctionnera car la connexion a été rétablie
        # entre temps (reconnexion automatique).
        value = yield self.cache.get("test")
        self.assertEquals(42, value)