<commit_before>#
# Copyright 2010-2015
#

import copy

from PySide import QtGui, QtCore

from pyflowgraph.graph_view import GraphView
from pyflowgraph.connection import Connection
from pyflowgraph.selection_rect import SelectionRect
from knode import KNode
from edit_index_widget import EditIndexWidget
from kraken.core.maths import Vec2
from kraken.core.kraken_system import KrakenSystem
from kraken.core.configs.config import Config

class KGraphView(GraphView):

    beginCopyData = QtCore.Signal()
    endCopyData = QtCore.Signal()

    beginPasteData = QtCore.Signal()
    endPasteData = QtCore.Signal()

    _clipboardData = None

    def __init__(self, parent=None):
        super(KGraphView, self).__init__(parent)
        self.__rig = None
        self.setAcceptDrops(True)

    def getRig(self):
        return self.__rig

    # ======
    # Graph
    # ======
    def displayGraph(self, rig):
        self.reset()

        self.__rig = rig

        guideComponents = self.__rig.getChildrenByType('Component')

        for component in guideComponents:
            self.addNode(KNode(self, component))

        for component in guideComponents:
            for i in range(component.getNumInputs()):
                componentInput = component.getInputByIndex(i)
                if componentInput.isConnected():
                    componentOutput = componentInput.getConnection()

                    self.connectPorts(
                        srcNode = componentOutput.getParent().getDecoratedName(), outputName = componentOutput.getName(),
                        tgtNode = component.getDecoratedName(), inputName=componentInput.getName()
                    )

        self.frameAllNodes()

    def addConnection(self, connection, emitSignal=True):

        result = super(KGraphView, self).addConnection(connection, emitSignal=emitSignal)

        # Indicate that this is an indexed connection.
        outPort = connection.getSrcPortCircle().getPort()
        inPort = connection.getDstPortCircle().getPort()
        if outPort is not None and inPort is not None and outPort.getDataType() != inPort.getDataType():
            if outPort.getDataType().startswith(inPort.getDataType()) and outPort.getDataType().endswith('[]'):
                connection.setPenStyle(QtCore.Qt.DashDotLine)
                connection.setPenWidth(2.5)

        return connection

    # =======
    # Events
    # =======
    def mousePressEvent(self, event):

        modifiers = QtGui.QApplication.keyboardModifiers()

        if event.button() == QtCore.Qt.MouseButton.RightButton:

            def graphItemAt(item):
                if isinstance(item, KNode):
                    return item
                if isinstance(item, Connection):
                    return item
                elif item is not None:
                    return graphItemAt(item.parentItem())
                return None

            graphicItem = graphItemAt(self.itemAt(event.pos()))
            pos = self.mapToScene(event.pos())

            if graphicItem is None:

                contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                contextMenu.setObjectName('rightClickContextMenu')
                contextMenu.setMinimumWidth(150)

                if self.getClipboardData() is not None:

                    def pasteSettings():
                        self.pasteSettings(pos)

                    def pasteSettingsMirrored():
                        self.pasteSettings(pos, mirrored=True)

                    contextMenu.addAction("Paste").triggered.connect(pasteSettings)
                    contextMenu.addAction("Paste Mirrored").triggered.connect(pasteSettingsMirrored)
                    contextMenu.addSeparator()

                graphViewWidget = self.getGraphViewWidget()
                contextMenu.addAction("Add Backdrop").triggered.connect(graphViewWidget.addBackdrop)
                contextMenu.popup(event.globalPos())

            if isinstance(graphicItem, KNode) and graphicItem.isSelected():
                contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                contextMenu.setObjectName('rightClickContextMenu')
                contextMenu.setMinimumWidth(150)

                def copySettings():
                    self.copySettings(pos)

                contextMenu.addAction("Copy").triggered.connect(copySettings)

                if self.getClipboardData() is not None:

                    def pasteSettings():
                        # Paste the settings, not modifying the location, because that will be used to determine symmetry.
                        graphicItem.getComponent().pasteData(self.getClipboardData()['components'][0], setLocation=False)

                    contextMenu.addSeparator()
                    contextMenu.addAction("Paste Data").triggered.connect(pasteSettings)

                contextMenu.popup(event.globalPos())

            elif isinstance(graphicItem, Connection):

                outPort = graphicItem.getSrcPortCircle().getPort()
                inPort = graphicItem.getDstPortCircle().getPort()
                if outPort.getDataType() != inPort.getDataType():

                    if outPort.getDataType().startswith(inPort.getDataType()) and outPort.getDataType().endswith('[]'):

                        globalPos = event.globalPos()
                        contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                        contextMenu.setObjectName('rightClickContextMenu')
                        contextMenu.setMinimumWidth(150)

                        def editIndex():
                            componentInput = graphicItem.getDstPortCircle().getPort().getComponentInput()
                            EditIndexWidget(componentInput, pos=globalPos, parent=self.getGraphViewWidget())

                        contextMenu.addAction("EditIndex").triggered.connect(editIndex)
                        contextMenu.popup(globalPos)

        elif event.button() is QtCore.Qt.MouseButton.LeftButton and self.itemAt(event.pos()) is None:
            self.beginNodeSelection.emit()
            self._manipulationMode = 1
            self._mouseDownSelection = copy.copy(self.getSelectedNodes())
            self.clearSelection(emitSignal=False)
            self._selectionRect = SelectionRect(graph=self, mouseDownPos=self.mapToScene(event.pos()))

        elif event.button() is QtCore.Qt.MouseButton.MiddleButton and modifiers == QtCore.Qt.AltModifier:

            self.setCursor(QtCore.Qt.OpenHandCursor)
            self._manipulationMode = 2
            self._lastPanPoint = self.mapToScene(event.pos())

        else:
            super(GraphView, self).mousePressEvent(event)

    def dragEnterEvent(self, event):
        textParts = event.mimeData().text().split(':')
        if textParts[0] == 'KrakenComponent':
            event.accept()
        else:
            event.setDropAction(QtCore.Qt.IgnoreAction)
            super(GraphView, self).dragEnterEvent(event)

    def dragMoveEvent(self, event):
        super(GraphView, self).dragMoveEvent(event)
        event.accept()

    def dropEvent(self, event):
        textParts = event.mimeData().text().split(':')
        if textParts[0] == 'KrakenComponent':
            componentClassName = textParts[1]

            # Add a component to the rig placed at the given position.
            dropPosition = self.mapToScene(event.pos())

            # construct the node and add it to the graph.
            krakenSystem = KrakenSystem.getInstance()
            componentClass = krakenSystem.getComponentClass( componentClassName )
            component = componentClass(parent=self.getRig())
            component.setGraphPos(Vec2(dropPosition.x(), dropPosition.y()))
            node = KNode(self, component)
            nodeColor = component.getComponentColor()
            node.setColor(QtGui.QColor(nodeColor[0], nodeColor[1], nodeColor[2], nodeColor[3]))
            self.addNode(node)

            event.acceptProposedAction()
        else:
            super(GraphView, self).dropEvent(event)

    def wheelEvent(self, event):

        zoom_mouse_scroll = self.window().preferences.getPreferenceValue('zoom_mouse_scroll')
        if zoom_mouse_scroll is True:
            super(KGraphView, self).wheelEvent(event)

    # =============
    # Copy / Paste
    # =============
    def getClipboardData(self):
        return self.__class__._clipboardData

    def copySettings(self, pos):
        clipboardData = {}

        copiedComponents = []
        nodes = self.getSelectedNodes()
        for node in nodes:
            copiedComponents.append(node.getComponent())

        componentsJson = []
        connectionsJson = []
        for component in copiedComponents:
            componentsJson.append(component.copyData())

            for i in range(component.getNumInputs()):
                componentInput = component.getInputByIndex(i)
                if componentInput.isConnected():
                    componentOutput = componentInput.getConnection()
                    connectionJson = {
                        'source': componentOutput.getParent().getDecoratedName() + '.' + componentOutput.getName(),
                        'target': component.getDecoratedName() + '.' + componentInput.getName()
                    }

                    connectionsJson.append(connectionJson)

        clipboardData = {
            'components': componentsJson,
            'connections': connectionsJson,
            'copyPos': pos
        }

        self.__class__._clipboardData = clipboardData

    def pasteSettings(self, pos, mirrored=False, createConnectionsToExistingNodes=True):

        clipboardData = self.__class__._clipboardData

        krakenSystem = KrakenSystem.getInstance()
        delta = pos - clipboardData['copyPos']
        self.clearSelection()
        pastedComponents = {}
        nameMapping = {}

        for componentData in clipboardData['components']:
            componentClass = krakenSystem.getComponentClass(componentData['class'])
            component = componentClass(parent=self.__rig)
            decoratedName = componentData['name'] + component.getNameDecoration()
            nameMapping[decoratedName] = decoratedName
            if mirrored:
                config = Config.getInstance()
                mirrorMap = config.getNameTemplate()['mirrorMap']
                component.setLocation(mirrorMap[componentData['location']])
                nameMapping[decoratedName] = componentData['name'] + component.getNameDecoration()
                component.pasteData(componentData, setLocation=False)
            else:
                component.pasteData(componentData, setLocation=True)
            graphPos = component.getGraphPos()
            component.setGraphPos(Vec2(graphPos.x + delta.x(), graphPos.y + delta.y()))
            node = KNode(self, component)
            self.addNode(node)
            self.selectNode(node, False)

            # save a dict of the nodes using the orignal names
            pastedComponents[nameMapping[decoratedName]] = component

        for connectionData in clipboardData['connections']:
            sourceComponentDecoratedName, outputName = connectionData['source'].split('.')
            targetComponentDecoratedName, inputName = connectionData['target'].split('.')

            sourceComponent = None

            # The connection is either between nodes that were pasted, or from pasted nodes
            # to unpasted nodes. We first check that the source component is in the pasted group
            # else use the node in the graph.
            if sourceComponentDecoratedName in nameMapping:
                sourceComponent = pastedComponents[nameMapping[sourceComponentDecoratedName]]
            else:
                if not createConnectionsToExistingNodes:
                    continue

                # When we support copying/pasting between rigs, then we may not find the source
                # node in the target rig.
                if not self.hasNode(sourceComponentDecoratedName):
                    continue
                node = self.getNode(sourceComponentDecoratedName)
                sourceComponent = node.getComponent()

            targetComponentDecoratedName = nameMapping[targetComponentDecoratedName]
            targetComponent = pastedComponents[targetComponentDecoratedName]

            outputPort = sourceComponent.getOutputByName(outputName)
            inputPort = targetComponent.getInputByName(inputName)

            inputPort.setConnection(outputPort)
            self.connectPorts(
                srcNode=sourceComponent.getDecoratedName(), outputName=outputPort.getName(),
                tgtNode=targetComponent.getDecoratedName(), inputName=inputPort.getName()
            )
<commit_msg>Kraken Graph view now tests preference on how to pan.<commit_after>#
# Copyright 2010-2015
#

import copy

from PySide import QtGui, QtCore

from pyflowgraph.graph_view import GraphView
from pyflowgraph.connection import Connection
from pyflowgraph.selection_rect import SelectionRect
from knode import KNode
from edit_index_widget import EditIndexWidget
from kraken.core.maths import Vec2
from kraken.core.kraken_system import KrakenSystem
from kraken.core.configs.config import Config

class KGraphView(GraphView):

    beginCopyData = QtCore.Signal()
    endCopyData = QtCore.Signal()

    beginPasteData = QtCore.Signal()
    endPasteData = QtCore.Signal()

    _clipboardData = None

    def __init__(self, parent=None):
        super(KGraphView, self).__init__(parent)
        self.__rig = None
        self.setAcceptDrops(True)

    def getRig(self):
        return self.__rig

    # ======
    # Graph
    # ======
    def displayGraph(self, rig):
        self.reset()

        self.__rig = rig

        guideComponents = self.__rig.getChildrenByType('Component')

        for component in guideComponents:
            self.addNode(KNode(self, component))

        for component in guideComponents:
            for i in range(component.getNumInputs()):
                componentInput = component.getInputByIndex(i)
                if componentInput.isConnected():
                    componentOutput = componentInput.getConnection()

                    self.connectPorts(
                        srcNode = componentOutput.getParent().getDecoratedName(), outputName = componentOutput.getName(),
                        tgtNode = component.getDecoratedName(), inputName=componentInput.getName()
                    )

        self.frameAllNodes()

    def addConnection(self, connection, emitSignal=True):

        result = super(KGraphView, self).addConnection(connection, emitSignal=emitSignal)

        # Indicate that this is an indexed connection.
        outPort = connection.getSrcPortCircle().getPort()
        inPort = connection.getDstPortCircle().getPort()
        if outPort is not None and inPort is not None and outPort.getDataType() != inPort.getDataType():
            if outPort.getDataType().startswith(inPort.getDataType()) and outPort.getDataType().endswith('[]'):
                connection.setPenStyle(QtCore.Qt.DashDotLine)
                connection.setPenWidth(2.5)

        return connection

    # =======
    # Events
    # =======
    def mousePressEvent(self, event):

        modifiers = QtGui.QApplication.keyboardModifiers()

        if event.button() == QtCore.Qt.MouseButton.RightButton:

            def graphItemAt(item):
                if isinstance(item, KNode):
                    return item
                if isinstance(item, Connection):
                    return item
                elif item is not None:
                    return graphItemAt(item.parentItem())
                return None

            graphicItem = graphItemAt(self.itemAt(event.pos()))
            pos = self.mapToScene(event.pos())

            if graphicItem is None:

                contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                contextMenu.setObjectName('rightClickContextMenu')
                contextMenu.setMinimumWidth(150)

                if self.getClipboardData() is not None:

                    def pasteSettings():
                        self.pasteSettings(pos)

                    def pasteSettingsMirrored():
                        self.pasteSettings(pos, mirrored=True)

                    contextMenu.addAction("Paste").triggered.connect(pasteSettings)
                    contextMenu.addAction("Paste Mirrored").triggered.connect(pasteSettingsMirrored)
                    contextMenu.addSeparator()

                graphViewWidget = self.getGraphViewWidget()
                contextMenu.addAction("Add Backdrop").triggered.connect(graphViewWidget.addBackdrop)
                contextMenu.popup(event.globalPos())

            if isinstance(graphicItem, KNode) and graphicItem.isSelected():
                contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                contextMenu.setObjectName('rightClickContextMenu')
                contextMenu.setMinimumWidth(150)

                def copySettings():
                    self.copySettings(pos)

                contextMenu.addAction("Copy").triggered.connect(copySettings)

                if self.getClipboardData() is not None:

                    def pasteSettings():
                        # Paste the settings, not modifying the location, because that will be used to determine symmetry.
                        graphicItem.getComponent().pasteData(self.getClipboardData()['components'][0], setLocation=False)

                    contextMenu.addSeparator()
                    contextMenu.addAction("Paste Data").triggered.connect(pasteSettings)

                contextMenu.popup(event.globalPos())

            elif isinstance(graphicItem, Connection):

                outPort = graphicItem.getSrcPortCircle().getPort()
                inPort = graphicItem.getDstPortCircle().getPort()
                if outPort.getDataType() != inPort.getDataType():

                    if outPort.getDataType().startswith(inPort.getDataType()) and outPort.getDataType().endswith('[]'):

                        globalPos = event.globalPos()
                        contextMenu = QtGui.QMenu(self.getGraphViewWidget())
                        contextMenu.setObjectName('rightClickContextMenu')
                        contextMenu.setMinimumWidth(150)

                        def editIndex():
                            componentInput = graphicItem.getDstPortCircle().getPort().getComponentInput()
                            EditIndexWidget(componentInput, pos=globalPos, parent=self.getGraphViewWidget())

                        contextMenu.addAction("EditIndex").triggered.connect(editIndex)
                        contextMenu.popup(globalPos)

        elif event.button() is QtCore.Qt.MouseButton.LeftButton and self.itemAt(event.pos()) is None:
            self.beginNodeSelection.emit()
            self._manipulationMode = 1
            self._mouseDownSelection = copy.copy(self.getSelectedNodes())
            self.clearSelection(emitSignal=False)
            self._selectionRect = SelectionRect(graph=self, mouseDownPos=self.mapToScene(event.pos()))

        elif event.button() is QtCore.Qt.MouseButton.MiddleButton:

            pan_with_alt = self.window().preferences.getPreferenceValue('pan_with_alt')
            if pan_with_alt is True and modifiers != QtCore.Qt.AltModifier:
                return

            self.setCursor(QtCore.Qt.OpenHandCursor)
            self._manipulationMode = 2
            self._lastPanPoint = self.mapToScene(event.pos())

        else:
            super(GraphView, self).mousePressEvent(event)

    def dragEnterEvent(self, event):
        textParts = event.mimeData().text().split(':')
        if textParts[0] == 'KrakenComponent':
            event.accept()
        else:
            event.setDropAction(QtCore.Qt.IgnoreAction)
            super(GraphView, self).dragEnterEvent(event)

    def dragMoveEvent(self, event):
        super(GraphView, self).dragMoveEvent(event)
        event.accept()

    def dropEvent(self, event):
        textParts = event.mimeData().text().split(':')
        if textParts[0] == 'KrakenComponent':
            componentClassName = textParts[1]

            # Add a component to the rig placed at the given position.
            dropPosition = self.mapToScene(event.pos())

            # construct the node and add it to the graph.
            krakenSystem = KrakenSystem.getInstance()
            componentClass = krakenSystem.getComponentClass( componentClassName )
            component = componentClass(parent=self.getRig())
            component.setGraphPos(Vec2(dropPosition.x(), dropPosition.y()))
            node = KNode(self, component)
            nodeColor = component.getComponentColor()
            node.setColor(QtGui.QColor(nodeColor[0], nodeColor[1], nodeColor[2], nodeColor[3]))
            self.addNode(node)

            event.acceptProposedAction()
        else:
            super(GraphView, self).dropEvent(event)

    def wheelEvent(self, event):

        zoom_mouse_scroll = self.window().preferences.getPreferenceValue('zoom_mouse_scroll')
        if zoom_mouse_scroll is True:
            super(KGraphView, self).wheelEvent(event)

    # =============
    # Copy / Paste
    # =============
    def getClipboardData(self):
        return self.__class__._clipboardData

    def copySettings(self, pos):
        clipboardData = {}

        copiedComponents = []
        nodes = self.getSelectedNodes()
        for node in nodes:
            copiedComponents.append(node.getComponent())

        componentsJson = []
        connectionsJson = []
        for component in copiedComponents:
            componentsJson.append(component.copyData())

            for i in range(component.getNumInputs()):
                componentInput = component.getInputByIndex(i)
                if componentInput.isConnected():
                    componentOutput = componentInput.getConnection()
                    connectionJson = {
                        'source': componentOutput.getParent().getDecoratedName() + '.' + componentOutput.getName(),
                        'target': component.getDecoratedName() + '.' + componentInput.getName()
                    }

                    connectionsJson.append(connectionJson)

        clipboardData = {
            'components': componentsJson,
            'connections': connectionsJson,
            'copyPos': pos
        }

        self.__class__._clipboardData = clipboardData

    def pasteSettings(self, pos, mirrored=False, createConnectionsToExistingNodes=True):

        clipboardData = self.__class__._clipboardData

        krakenSystem = KrakenSystem.getInstance()
        delta = pos - clipboardData['copyPos']
        self.clearSelection()
        pastedComponents = {}
        nameMapping = {}

        for componentData in clipboardData['components']:
            componentClass = krakenSystem.getComponentClass(componentData['class'])
            component = componentClass(parent=self.__rig)
            decoratedName = componentData['name'] + component.getNameDecoration()
            nameMapping[decoratedName] = decoratedName
            if mirrored:
                config = Config.getInstance()
                mirrorMap = config.getNameTemplate()['mirrorMap']
                component.setLocation(mirrorMap[componentData['location']])
                nameMapping[decoratedName] = componentData['name'] + component.getNameDecoration()
                component.pasteData(componentData, setLocation=False)
            else:
                component.pasteData(componentData, setLocation=True)
            graphPos = component.getGraphPos()
            component.setGraphPos(Vec2(graphPos.x + delta.x(), graphPos.y + delta.y()))
            node = KNode(self, component)
            self.addNode(node)
            self.selectNode(node, False)

            # save a dict of the nodes using the orignal names
            pastedComponents[nameMapping[decoratedName]] = component

        for connectionData in clipboardData['connections']:
            sourceComponentDecoratedName, outputName = connectionData['source'].split('.')
            targetComponentDecoratedName, inputName = connectionData['target'].split('.')

            sourceComponent = None

            # The connection is either between nodes that were pasted, or from pasted nodes
            # to unpasted nodes. We first check that the source component is in the pasted group
            # else use the node in the graph.
            if sourceComponentDecoratedName in nameMapping:
                sourceComponent = pastedComponents[nameMapping[sourceComponentDecoratedName]]
            else:
                if not createConnectionsToExistingNodes:
                    continue

                # When we support copying/pasting between rigs, then we may not find the source
                # node in the target rig.
                if not self.hasNode(sourceComponentDecoratedName):
                    continue
                node = self.getNode(sourceComponentDecoratedName)
                sourceComponent = node.getComponent()

            targetComponentDecoratedName = nameMapping[targetComponentDecoratedName]
            targetComponent = pastedComponents[targetComponentDecoratedName]

            outputPort = sourceComponent.getOutputByName(outputName)
            inputPort = targetComponent.getInputByName(inputName)

            inputPort.setConnection(outputPort)
            self.connectPorts(
                srcNode=sourceComponent.getDecoratedName(), outputName=outputPort.getName(),
                tgtNode=targetComponent.getDecoratedName(), inputName=inputPort.getName()
            )
<|endoftext|>


<commit_before>#!/usr/bin/python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#    Thibault Cohen, thibault.cohen@savoirfairelinux.com
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

"""
Collectd Plugin for Receiver or arbiter
"""

import os
import time
from itertools import izip

from shinken.basemodule import BaseModule
from shinken.external_command import ExternalCommand
from shinken.log import logger

properties = {
    'daemons': ['arbiter', 'receiver'],
    'type': 'collectd',
    'external': True,
    }

DEFAULT_PORT = 25826
DEFAULT_MULTICAST_IP = "239.192.74.66"
BUFFER_SIZE = 4096

def get_instance(plugin):
    """ This function is called by the module manager
    to get an instance of this module
    """
    if hasattr(plugin, "multicast"):
        multicast = plugin.multicast.lower() in ("yes", "true", "1")
    else:
        multicast = False

    if hasattr(plugin, 'host'):
        host = plugin.host
    else:
        host = DEFAULT_MULTICAST_IP
        multicast = True

    if hasattr(plugin, 'port'):
        port = int(plugin.port)
    else:
        port = DEFAULT_PORT

    if hasattr(plugin, 'grouped_collectd_plugins'):
        grouped_collectd_plugins = [name.strip()
                                    for name in plugin.grouped_collectd_plugins.split(',')]
    else:
        grouped_collectd_plugins = []

    logger.info("[Collectd] Using host=%s port=%d multicast=%d" % (host, port, multicast))

    instance = Collectd_arbiter(plugin, host, port, multicast, grouped_collectd_plugins)
    return instance

import socket
import struct
import time
from StringIO import StringIO

# Collectd message types
TYPE_HOST = 0x0000
TYPE_TIME = 0x0001
TYPE_TIME_HR = 0x0008
TYPE_PLUGIN = 0x0002
TYPE_PLUGIN_INSTANCE = 0x0003
TYPE_TYPE = 0x0004
TYPE_TYPE_INSTANCE = 0x0005
TYPE_VALUES = 0x0006
TYPE_INTERVAL = 0x0007
TYPE_INTERVAL_HR = 0x0009
TYPE_MESSAGE = 0x0100
TYPE_SEVERITY = 0x0101

# DS kinds
DS_TYPE_COUNTER = 0
DS_TYPE_GAUGE = 1
DS_TYPE_DERIVE = 2
DS_TYPE_ABSOLUTE = 3

header = struct.Struct("!2H")
number = struct.Struct("!Q")
short = struct.Struct("!H")
double = struct.Struct("<d")

elements = {}

def decode_values(pktype, plen, buf):
    """ Decode values from collectd requests """
    nvalues = short.unpack_from(buf, header.size)[0]
    off = header.size + short.size + nvalues
    valskip = double.size

    # check the packet head
    if ((valskip + 1) * nvalues + short.size + header.size) != plen:
        return []
    if double.size != number.size:
        return []

    result = []
    for dstype in map(ord, buf[header.size + short.size:off]):
        if (dstype == DS_TYPE_COUNTER or dstype == DS_TYPE_DERIVE or dstype == DS_TYPE_ABSOLUTE):
            v = (dstype, number.unpack_from(buf, off)[0])
            result.append(v)
            off += valskip
        elif dstype == DS_TYPE_GAUGE:
            v = (dstype, double.unpack_from(buf, off)[0])
            result.append(v)
            off += valskip
        else:
            logger.warning("[Collectd] DS type %i unsupported" % dstype)

    return result

# Get a u64
def decode_number(pktype, pklen, buf):
    """ Decode number typed value """
    return number.unpack_from(buf, header.size)[0]

# Get a simple char
def decode_string(msgtype, pklen, buf):
    """ Decode string typed value """
    return buf[header.size:pklen-1]

# Mapping of message types to decoding functions.
decoder_mapping = {
    TYPE_VALUES: decode_values,
    TYPE_TIME: decode_number,
    TYPE_TIME_HR: decode_number,
    TYPE_INTERVAL: decode_number,
    TYPE_INTERVAL_HR: decode_number,
    TYPE_HOST: decode_string,
    TYPE_PLUGIN: decode_string,
    TYPE_PLUGIN_INSTANCE: decode_string,
    TYPE_TYPE: decode_string,
    TYPE_TYPE_INSTANCE: decode_string,
    TYPE_MESSAGE: decode_string,
    TYPE_SEVERITY: decode_number,
}

def decode_packet(buf):
    """ decode packet from collectd requests """
    off = 0
    buflen = len(buf)
    while off < buflen:
        pktype, pklen = header.unpack_from(buf, off)

        if pklen > buflen - off:
            raise ValueError("Packet too long?")

        if pktype not in decoder_mapping:
            raise ValueError("Message type %i not recognized" % pktype)

        v = decoder_mapping[pktype](pktype, pklen, buf[off:])
        yield pktype, v
        off += pklen

class Data(list, object):
    """ This class will transform datas

    :grouped_collectd_plugins: list of collecd plugins to group
    """
    def __init__(self, grouped_collectd_plugins=[],  **kw):

        self.kind = 0
        self.time = 0
        self.interval = 0
        self.host = None
        self.plugin = ''
        self.plugininstance = ''
        self.type = ''
        self.typeinstance = ''
        self.message = ''
        self.severity = 0
        self.values = []
        self.grouped_collectd_plugins = grouped_collectd_plugins

    def __str__(self):
        """ Return a readable format of a Data object """
        return "[%i] %s" % (self.time, self.values)

    def get_srv_desc(self):
        """ Determine service name from collectd datas """
        r = self.plugin
        if not r in self.grouped_collectd_plugins:
            if self.plugininstance:
                r += '-' + self.plugininstance
        return r

    def get_message(self):
        """ Get message of a Data object
        """
        return self.message

    def get_kind(self):
        """ Get kind of a Data object
        """
        return self.kind

    def get_metric_name(self):
        """ Determine perf data name from collectd datas
        """
        r = self.type
        if self.plugin in self.grouped_collectd_plugins:
            if self.plugininstance:
                r += '-' + self.plugininstance
        if self.typeinstance:
            r += '-' + self.typeinstance
        return r

    def get_metric_values(self):
        """ Determine perf datas from collectd datas
        """
        if len(self.values) == 0:
            return None
        return self.values

    def get_name(self):
        """ Determine data name from collectd datas
        """
        if not self.host:
            return None
        srv_desc = self.get_srv_desc()
        r = '%s;%s' % (self.host, srv_desc)
        return r

    def get_time(self):
        """ Return data time from collectd datas
        """
        return self.time

    def get_message_command(self):
        """ Return data severity (exit code) from collectd datas
        """
        now = int(time.time())
        if self.severity == 4:  # OK
            returncode = 0
        elif self.severity == 1:
            returncode = 2
        elif self.severity == 2:
            returncode = 1
        else:
            returncode = 3
        return '[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s' % (now,
                                                                  self.host,
                                                                  self.get_srv_desc(),
                                                                  returncode,
                                                                  self.message,
                                                                  )

class CollectdServer(object):
    """ Collectd server
    This class listen and and handle collectd requests

    :host:                     Bind address
    :port:                     Bind port
    :multicast:                Enable multisite
    :grouped_collectd_plugins: List of collecd plugins to group
    """
    def __init__(self, host, port, multicast, grouped_collectd_plugins=[]):
        self.host = host
        self.port = port
        self.grouped_collectd_plugins = grouped_collectd_plugins

        logger.info("[Collectd] Opening socket")
        family, socktype, proto, _, sockaddr = socket.getaddrinfo(None if multicast else self.host,
                                                                  self.port,
                                                                  socket.AF_UNSPEC,
                                                                  socket.SOCK_DGRAM,
                                                                  0,
                                                                  socket.AI_PASSIVE,
                                                                  )[0]

        self._sock = socket.socket(family, socktype, proto)
        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._sock.bind(sockaddr)
        logger.info("[Collectd] Socket open")

        if multicast:
            if hasattr(socket, "SO_REUSEPORT"):
                self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

            val = struct.pack("4sl", socket.inet_aton(self.host), socket.INADDR_ANY)

            self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, val)
            self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)

        logger.info("[Collectd] Socket is opened")

    def interpret_opcodes(self, iterable):
        """ Decode some stuff from Collectd
        """
        d = Data(self.grouped_collectd_plugins)

        for kind, data in iterable:
            d.kind = kind
            if kind == TYPE_TIME:
                d.time = data
            elif kind == TYPE_TIME_HR:
                d.time = data >> 30
            elif kind == TYPE_INTERVAL:
                d.interval = data
            elif kind == TYPE_INTERVAL_HR:
                d.interval = data >> 30
            elif kind == TYPE_HOST:
                d.host = data
            elif kind == TYPE_PLUGIN:
                d.plugin = data
            elif kind == TYPE_PLUGIN_INSTANCE:
                d.plugininstance = data
            elif kind == TYPE_TYPE:
                d.type = data
            elif kind == TYPE_TYPE_INSTANCE:
                d.typeinstance = data
            elif kind == TYPE_VALUES:
                d.values = data
            elif kind == TYPE_MESSAGE:
                d.message = data
            elif kind == TYPE_SEVERITY:
                d.severity = data
            yield d

    def receive(self):
        """ Read socket
        """
        return self._sock.recv(BUFFER_SIZE)

    def decode(self, buf=None):
        """ Return a decode packet
        """
        if buf is None:
            buf = self.receive()
        return decode_packet(buf)

    def read(self, iterable=None):
        """ Return a list of decoded packets
        """
        if iterable is None:
            iterable = self.decode()
        if isinstance(iterable, basestring):
            iterable = self.decode(iterable)
        return self.interpret_opcodes(iterable)

class Element(object):
    """ Element store service name and all perfdatas before send it in a external command """
    def __init__(self, host_name, sdesc, interval):
        self.host_name = host_name
        self.sdesc = sdesc
        self.perf_datas = {}
        self.last_update = 0.0
        self.interval = interval
        self.got_new_data = False

    def add_perf_data(self, mname, mvalues, mtime):
        """ Add perf datas to the message to send to Shinken """
        if not mvalues:
            return

        r = []
        if mname not in self.perf_datas:
            for (dstype, newrawval) in mvalues:
                r.append((dstype, newrawval, newrawval, mtime))
        else:
            oldvalues = self.perf_datas[mname]

            for (olddstype, oldrawval, oldval, oldtime), (dstype, newrawval) in izip(oldvalues, mvalues):
                difftime = mtime - oldtime
                if difftime < 1:
                    continue
                if dstype == DS_TYPE_COUNTER or dstype == DS_TYPE_DERIVE or dstype == DS_TYPE_ABSOLUTE:
                    r.append((dstype, newrawval, (newrawval - oldrawval) / float(difftime), mtime))
                elif dstype == DS_TYPE_GAUGE:
                    r.append((dstype, newrawval, newrawval, mtime))

        self.perf_datas[mname] = r
        self.got_new_data = True

    def get_command(self):
        """ Prepare the external command for Shinken """
        if len(self.perf_datas) == 0:
            return None

        if not self.got_new_data:
            return None

        now = int(time.time())
        if now > self.last_update + self.interval:
            r = '[%d] PROCESS_SERVICE_OUTPUT;%s;%s;CollectD| ' % (now, self.host_name, self.sdesc)
            for (k, v) in self.perf_datas.iteritems():
                for i, w in enumerate(v):
                    if len(v) > 1:
                        r += '%s_%d=%s ' % (k, i, str(w[2]))
                    else:
                        r += '%s=%s ' % (k, str(w[2]))
            logger.debug('Updating: %s - %s ' % (self.host_name, self.sdesc))
#            self.perf_datas.clear()
            self.last_update = now
            self.got_new_data = False
            return r

class Collectd_arbiter(BaseModule):
    """ Main class for this collecitd module """
    def __init__(self, modconf, host, port, multicast, grouped_collectd_plugins=[]):
        BaseModule.__init__(self, modconf)
        self.host = host
        self.port = port
        self.multicast = multicast
        self.grouped_collectd_plugins = grouped_collectd_plugins

    # When you are in "external" mode, that is the main loop of your process
    def main(self):
        """ Plugin main loop """
        self.set_proctitle(self.name)
        self.set_exit_handler()

        try:
            cs = CollectdServer(self.host, self.port, self.multicast, self.grouped_collectd_plugins)
            while True:
                # Each second we are looking at sending old elements
                for e in elements.values():
                    c = e.get_command()
                    if c is not None:
                        logger.debug("[Collectd] Got %s" % c)
                        self.from_q.put(ExternalCommand(c))
                for item in cs.read():
                    logger.debug("[Collectd] %s: %s" % (item, item.__dict__))
                    n = item.get_name()
                    if n and n not in elements:
                        e = Element(item.host,
                                    item.get_srv_desc(),
                                    item.interval)
                        elements[n] = e
                    e = elements[n]
                    if item.get_kind() == TYPE_VALUES:
                        e.add_perf_data(item.get_metric_name(),
                                        item.get_metric_values(),
                                        item.get_time())
                    elif item.get_kind() == TYPE_MESSAGE:
                        c = item.get_message_command()
                        if c is not None:
                            self.from_q.put(ExternalCommand(c))
        except Exception, e:
            logger.error("[Collectd] exception: %s" % str(e))
        except ValueError, exp:
            logger.error("[Collectd] Read error: %s" % exp)
<commit_msg>Fix crash when receiving notifications<commit_after>#!/usr/bin/python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#    Thibault Cohen, thibault.cohen@savoirfairelinux.com
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

"""
Collectd Plugin for Receiver or arbiter
"""

import os
import time
from itertools import izip

from shinken.basemodule import BaseModule
from shinken.external_command import ExternalCommand
from shinken.log import logger

properties = {
    'daemons': ['arbiter', 'receiver'],
    'type': 'collectd',
    'external': True,
    }

DEFAULT_PORT = 25826
DEFAULT_MULTICAST_IP = "239.192.74.66"
BUFFER_SIZE = 4096

def get_instance(plugin):
    """ This function is called by the module manager
    to get an instance of this module
    """
    if hasattr(plugin, "multicast"):
        multicast = plugin.multicast.lower() in ("yes", "true", "1")
    else:
        multicast = False

    if hasattr(plugin, 'host'):
        host = plugin.host
    else:
        host = DEFAULT_MULTICAST_IP
        multicast = True

    if hasattr(plugin, 'port'):
        port = int(plugin.port)
    else:
        port = DEFAULT_PORT

    if hasattr(plugin, 'grouped_collectd_plugins'):
        grouped_collectd_plugins = [name.strip()
                                    for name in plugin.grouped_collectd_plugins.split(',')]
    else:
        grouped_collectd_plugins = []

    logger.info("[Collectd] Using host=%s port=%d multicast=%d" % (host, port, multicast))

    instance = Collectd_arbiter(plugin, host, port, multicast, grouped_collectd_plugins)
    return instance

import socket
import struct
import time
from StringIO import StringIO

# Collectd message types
TYPE_HOST = 0x0000
TYPE_TIME = 0x0001
TYPE_TIME_HR = 0x0008
TYPE_PLUGIN = 0x0002
TYPE_PLUGIN_INSTANCE = 0x0003
TYPE_TYPE = 0x0004
TYPE_TYPE_INSTANCE = 0x0005
TYPE_VALUES = 0x0006
TYPE_INTERVAL = 0x0007
TYPE_INTERVAL_HR = 0x0009
TYPE_MESSAGE = 0x0100
TYPE_SEVERITY = 0x0101

# DS kinds
DS_TYPE_COUNTER = 0
DS_TYPE_GAUGE = 1
DS_TYPE_DERIVE = 2
DS_TYPE_ABSOLUTE = 3

header = struct.Struct("!2H")
number = struct.Struct("!Q")
short = struct.Struct("!H")
double = struct.Struct("<d")

elements = {}

def decode_values(pktype, plen, buf):
    """ Decode values from collectd requests """
    nvalues = short.unpack_from(buf, header.size)[0]
    off = header.size + short.size + nvalues
    valskip = double.size

    # check the packet head
    if ((valskip + 1) * nvalues + short.size + header.size) != plen:
        return []
    if double.size != number.size:
        return []

    result = []
    for dstype in map(ord, buf[header.size + short.size:off]):
        if (dstype == DS_TYPE_COUNTER or dstype == DS_TYPE_DERIVE or dstype == DS_TYPE_ABSOLUTE):
            v = (dstype, number.unpack_from(buf, off)[0])
            result.append(v)
            off += valskip
        elif dstype == DS_TYPE_GAUGE:
            v = (dstype, double.unpack_from(buf, off)[0])
            result.append(v)
            off += valskip
        else:
            logger.warning("[Collectd] DS type %i unsupported" % dstype)

    return result

# Get a u64
def decode_number(pktype, pklen, buf):
    """ Decode number typed value """
    return number.unpack_from(buf, header.size)[0]

# Get a simple char
def decode_string(msgtype, pklen, buf):
    """ Decode string typed value """
    return buf[header.size:pklen-1]

# Mapping of message types to decoding functions.
decoder_mapping = {
    TYPE_VALUES: decode_values,
    TYPE_TIME: decode_number,
    TYPE_TIME_HR: decode_number,
    TYPE_INTERVAL: decode_number,
    TYPE_INTERVAL_HR: decode_number,
    TYPE_HOST: decode_string,
    TYPE_PLUGIN: decode_string,
    TYPE_PLUGIN_INSTANCE: decode_string,
    TYPE_TYPE: decode_string,
    TYPE_TYPE_INSTANCE: decode_string,
    TYPE_MESSAGE: decode_string,
    TYPE_SEVERITY: decode_number,
}

def decode_packet(buf):
    """ decode packet from collectd requests """
    off = 0
    buflen = len(buf)
    while off < buflen:
        pktype, pklen = header.unpack_from(buf, off)

        if pklen > buflen - off:
            raise ValueError("Packet too long?")

        if pktype not in decoder_mapping:
            raise ValueError("Message type %i not recognized" % pktype)

        v = decoder_mapping[pktype](pktype, pklen, buf[off:])
        yield pktype, v
        off += pklen

class Data(list, object):
    """ This class will transform datas

    :grouped_collectd_plugins: list of collecd plugins to group
    """
    def __init__(self, grouped_collectd_plugins=[],  **kw):

        self.kind = 0
        self.time = 0
        self.interval = 0
        self.host = None
        self.plugin = ''
        self.plugininstance = ''
        self.type = ''
        self.typeinstance = ''
        self.message = ''
        self.severity = 0
        self.values = []
        self.grouped_collectd_plugins = grouped_collectd_plugins

    def __str__(self):
        """ Return a readable format of a Data object """
        return "[%i] %s" % (self.time, self.values)

    def get_srv_desc(self):
        """ Determine service name from collectd datas """
        r = self.plugin
        if not r in self.grouped_collectd_plugins:
            if self.plugininstance:
                r += '-' + self.plugininstance
        return r

    def get_message(self):
        """ Get message of a Data object
        """
        return self.message

    def get_kind(self):
        """ Get kind of a Data object
        """
        return self.kind

    def get_metric_name(self):
        """ Determine perf data name from collectd datas
        """
        r = self.type
        if self.plugin in self.grouped_collectd_plugins:
            if self.plugininstance:
                r += '-' + self.plugininstance
        if self.typeinstance:
            r += '-' + self.typeinstance
        return r

    def get_metric_values(self):
        """ Determine perf datas from collectd datas
        """
        if len(self.values) == 0:
            return None
        return self.values

    def get_name(self):
        """ Determine data name from collectd datas
        """
        if not self.host:
            return None
        srv_desc = self.get_srv_desc()
        r = '%s;%s' % (self.host, srv_desc)
        return r

    def get_time(self):
        """ Return data time from collectd datas
        """
        return self.time

    def get_message_command(self):
        """ Return data severity (exit code) from collectd datas
        """
        now = int(time.time())
        if self.severity == 4:  # OK
            returncode = 0
        elif self.severity == 1:
            returncode = 2
        elif self.severity == 2:
            returncode = 1
        else:
            returncode = 3
        return '[%d] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s' % (now,
                                                                  self.host,
                                                                  self.get_srv_desc(),
                                                                  returncode,
                                                                  self.message,
                                                                  )

class CollectdServer(object):
    """ Collectd server
    This class listen and and handle collectd requests

    :host:                     Bind address
    :port:                     Bind port
    :multicast:                Enable multisite
    :grouped_collectd_plugins: List of collecd plugins to group
    """
    def __init__(self, host, port, multicast, grouped_collectd_plugins=[]):
        self.host = host
        self.port = port
        self.grouped_collectd_plugins = grouped_collectd_plugins

        logger.info("[Collectd] Opening socket")
        family, socktype, proto, _, sockaddr = socket.getaddrinfo(None if multicast else self.host,
                                                                  self.port,
                                                                  socket.AF_UNSPEC,
                                                                  socket.SOCK_DGRAM,
                                                                  0,
                                                                  socket.AI_PASSIVE,
                                                                  )[0]

        self._sock = socket.socket(family, socktype, proto)
        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self._sock.bind(sockaddr)
        logger.info("[Collectd] Socket open")

        if multicast:
            if hasattr(socket, "SO_REUSEPORT"):
                self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

            val = struct.pack("4sl", socket.inet_aton(self.host), socket.INADDR_ANY)

            self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, val)
            self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)

        logger.info("[Collectd] Socket is opened")

    def interpret_opcodes(self, iterable):
        """ Decode some stuff from Collectd
        """
        d = Data(self.grouped_collectd_plugins)

        for kind, data in iterable:
            d.kind = kind
            if kind == TYPE_TIME:
                d.time = data
            elif kind == TYPE_TIME_HR:
                d.time = data >> 30
            elif kind == TYPE_INTERVAL:
                d.interval = data
            elif kind == TYPE_INTERVAL_HR:
                d.interval = data >> 30
            elif kind == TYPE_HOST:
                d.host = data
            elif kind == TYPE_PLUGIN:
                d.plugin = data
            elif kind == TYPE_PLUGIN_INSTANCE:
                d.plugininstance = data
            elif kind == TYPE_TYPE:
                d.type = data
            elif kind == TYPE_TYPE_INSTANCE:
                d.typeinstance = data
            elif kind == TYPE_SEVERITY:
                d.severity = data
            elif kind == TYPE_VALUES:
                d.values = data
                yield d
            elif kind == TYPE_MESSAGE:
                d.message = data
                yield d

    def receive(self):
        """ Read socket
        """
        return self._sock.recv(BUFFER_SIZE)

    def decode(self, buf=None):
        """ Return a decode packet
        """
        if buf is None:
            buf = self.receive()
        return decode_packet(buf)

    def read(self, iterable=None):
        """ Return a list of decoded packets
        """
        if iterable is None:
            iterable = self.decode()
        if isinstance(iterable, basestring):
            iterable = self.decode(iterable)
        return self.interpret_opcodes(iterable)

class Element(object):
    """ Element store service name and all perfdatas before send it in a external command """
    def __init__(self, host_name, sdesc, interval):
        self.host_name = host_name
        self.sdesc = sdesc
        self.perf_datas = {}
        self.last_update = 0.0
        self.interval = interval
        self.got_new_data = False

    def add_perf_data(self, mname, mvalues, mtime):
        """ Add perf datas to the message to send to Shinken """
        if not mvalues:
            return

        r = []
        if mname not in self.perf_datas:
            for (dstype, newrawval) in mvalues:
                r.append((dstype, newrawval, newrawval, mtime))
        else:
            oldvalues = self.perf_datas[mname]

            for (olddstype, oldrawval, oldval, oldtime), (dstype, newrawval) in izip(oldvalues, mvalues):
                difftime = mtime - oldtime
                if difftime < 1:
                    continue
                if dstype == DS_TYPE_COUNTER or dstype == DS_TYPE_DERIVE or dstype == DS_TYPE_ABSOLUTE:
                    r.append((dstype, newrawval, (newrawval - oldrawval) / float(difftime), mtime))
                elif dstype == DS_TYPE_GAUGE:
                    r.append((dstype, newrawval, newrawval, mtime))

        self.perf_datas[mname] = r
        self.got_new_data = True

    def get_command(self):
        """ Prepare the external command for Shinken """
        if len(self.perf_datas) == 0:
            return None

        if not self.got_new_data:
            return None

        now = int(time.time())
        if now > self.last_update + self.interval:
            r = '[%d] PROCESS_SERVICE_OUTPUT;%s;%s;CollectD| ' % (now, self.host_name, self.sdesc)
            for (k, v) in self.perf_datas.iteritems():
                for i, w in enumerate(v):
                    if len(v) > 1:
                        r += '%s_%d=%s ' % (k, i, str(w[2]))
                    else:
                        r += '%s=%s ' % (k, str(w[2]))
            logger.debug('Updating: %s - %s ' % (self.host_name, self.sdesc))
#            self.perf_datas.clear()
            self.last_update = now
            self.got_new_data = False
            return r

class Collectd_arbiter(BaseModule):
    """ Main class for this collecitd module """
    def __init__(self, modconf, host, port, multicast, grouped_collectd_plugins=[]):
        BaseModule.__init__(self, modconf)
        self.host = host
        self.port = port
        self.multicast = multicast
        self.grouped_collectd_plugins = grouped_collectd_plugins

    # When you are in "external" mode, that is the main loop of your process
    def main(self):
        """ Plugin main loop """
        self.set_proctitle(self.name)
        self.set_exit_handler()

        try:
            cs = CollectdServer(self.host, self.port, self.multicast, self.grouped_collectd_plugins)
            while True:
                # Each second we are looking at sending old elements
                for e in elements.values():
                    c = e.get_command()
                    if c is not None:
                        logger.debug("[Collectd] Got %s" % c)
                        self.from_q.put(ExternalCommand(c))
                for item in cs.read():
                    logger.debug("[Collectd] %s: %s" % (item, item.__dict__))
                    n = item.get_name()
                    if n and n not in elements:
                        e = Element(item.host,
                                    item.get_srv_desc(),
                                    item.interval)
                        elements[n] = e
                    e = elements[n]
                    if item.get_kind() == TYPE_VALUES:
                        e.add_perf_data(item.get_metric_name(),
                                        item.get_metric_values(),
                                        item.get_time())
                    elif item.get_kind() == TYPE_MESSAGE:
                        c = item.get_message_command()
                        if c is not None:
                            self.from_q.put(ExternalCommand(c))
        except Exception, e:
            logger.error("[Collectd] exception: %s" % str(e))
        except ValueError, exp:
            logger.error("[Collectd] Read error: %s" % exp)
<|endoftext|>


<commit_before>#!/usr/bin/python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

# This Class is a plugin for the Shinken Arbiter. It read a json file
# with all links between objects. Update them (create/delete) at the
# launch or at fly

import time
import os
import subprocess
from shinken.log import logger
# Try to load the json (2.5 and higher) or
# the simplejson if failed (python2.4)
try:
    import json
except ImportError:
    # For old Python version, load
    # simple json (it can be hard json?! It's 2 functions guy!)
    try:
        import simplejson as json
    except ImportError:
        logger.error("[Hot dependencies] Error: you need the json or simplejson module for this script")
        raise

from shinken.basemodule import BaseModule
from shinken.external_command import ExternalCommand

properties = {
    'daemons': ['arbiter'],
    'type': 'hot_dependencies',
    'external': False,
    'phases': ['late_configuration'],
    }

# called by the plugin manager to get a broker
def get_instance(plugin):
    logger.info("[Hot dependencies] Get a Hot dependencies module for arbiter with plugin %s" % plugin.get_name())
    mapping_file = getattr(plugin, 'mapping_file', '')
    mapping_command = getattr(plugin, 'mapping_command', '')
    mapping_command_interval = int(getattr(plugin, 'mapping_command_interval', '60'))
    mapping_command_timeout = int(getattr(plugin, 'mapping_command_timeout', '300'))
    in_debug = getattr(plugin, 'debug', '0') == '1'
    instance = Hot_dependencies_arbiter(plugin, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout, in_debug)
    return instance

# Get hosts and/or services dep by launching a command
# or read a flat file as json format that got theses links
class Hot_dependencies_arbiter(BaseModule):
    def __init__(self, modconf, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout, in_debug=False):
        BaseModule.__init__(self, modconf)
        self.mapping_file = mapping_file
        self.last_update = 0
        self.last_mapping = set()
        self.mapping = set()
        # The external process part
        self.mapping_command = mapping_command
        self.mapping_command_interval = mapping_command_interval
        self.last_cmd_launch = 0
        self.process = None
        self.mapping_command_timeout = mapping_command_timeout
        self.in_debug = in_debug

    # Called by Arbiter to say 'let's prepare yourself guy'
    def init(self):
        logger.info("I open the HOT dependency module")
        # Remember what we add

    def _is_file_existing(self):
        return os.path.exists(self.mapping_file)

    # Look is the mapping filechanged since the last lookup
    def _is_mapping_file_changed(self):
        try:
            last_update = os.path.getmtime(self.mapping_file)
            if last_update > self.last_update:
                self.last_update = last_update
                return True
        except OSError, exp:  # Maybe the file got problem, we bypass here
            logger.error("[Hot dependencies] Error: %s" % str(exp))
        return False

    # Read the mapping file and update our internal mappings
    def _update_mapping(self):
        f = open(self.mapping_file, 'rb')
        mapping = json.loads(f.read())
        f.close()
        self.last_mapping = self.mapping
        # mapping is a list of list, we want a set of tuples
        # because list cannot be hased for a set pass
        self.mapping = set()
        for e in mapping:
            son, father = e
            self.mapping.add((tuple(son), tuple(father)))

    # Maybe the file is updated, but the mapping is the same
    # if not, look at addition and remove objects
    def _got_mapping_changes(self):
        additions = self.mapping - self.last_mapping
        removed = self.last_mapping - self.mapping

        return additions, removed

    # Launch the external command to generate the file
    def _launch_command(self):
        logger.debug("[Hot dependencies] Launching command %s" % self.mapping_command)
        self.last_cmd_launch = int(time.time())

        # windows subprocess do not want us to close fd
        do_close_fd = (not os.name == 'nt')
        try:
            self.process = subprocess.Popen(
                self.mapping_command,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                close_fds=do_close_fd, shell=True)
        except OSError, exp:
            self.error("Fail Launching the command %s: %s" % (self.mapping_command, exp))

    # Look if the command is finished or not
    def _watch_command_finished(self):
        if self.process.poll() is None:
            # Ok, still unfinished, look if in timeout
            now = time.time()
            if (now - self.last_cmd_launch) > self.mapping_command_timeout:
                logger.debug("[Hot dependencies] The external command go in timeout!")
        else:
            logger.info("The external command is done!")
            # it's finished! Cool
            (stdoutdata, stderrdata) = self.process.communicate()
            if self.process.returncode != 0:
                logger.debug("[Hot dependencies] The command return in error: %s \n %s" % (stderrdata, stdoutdata))
            self.process = None

    def tick_external_command(self):
        now = int(time.time())

        # If we got no in progress command, look if we should launch a new one
        if self.process is None:
            if now - self.last_cmd_launch > self.mapping_command_interval:
                if self.mapping_command_interval != 0 and self.mapping_command != '':
                    logger.debug("[Hot dependencies] The command launch is too old, launch a new one")
                    self._launch_command()
#            else:
#                print "The last cmd launch is too early", now - self.last_cmd_launch, self.mapping_command_interval
        else:
            # We got one in progress, we should look if it's finished or not
            self._watch_command_finished()

    # Ok, main function that will load dep from a json file
    def hook_late_configuration(self, arb):
        # We will return external commands to the arbiter, so
        # it can just manage it easily and in a generic way
        ext_cmds = []

        # If the file do not exist, we launch the command
        # and we bail out
        if not self._is_file_existing():
            self._launch_command()
            return

        self._is_mapping_file_changed()
        self._update_mapping()
        additions, removed = self._got_mapping_changes()

        for (father_k, son_k) in additions:
            son_type, son_name = son_k
            father_type, father_name = father_k
            logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
            if son_type == 'host' and father_type == 'host':
                son = arb.conf.hosts.find_by_name(son_name)
                father = arb.conf.hosts.find_by_name(father_name)
                if son is not None and father is not None:
                    logger.debug("[Hot dependencies] Found! %s %s" % (son_name, father_name))
                    if not son.is_linked_with_host(father):
                        logger.debug("[Hot dependencies] Doing simple link between %s and %s" % (son.get_name(), father.get_name()))
                        # Add a dep link between the son and the father
                        son.add_host_act_dependency(father, ['w', 'u', 'd'], None, True)
                else:
                    logger.debug("[Hot dependencies] Missing one of %s %s" % (son_name, father_name))
# TODO : Define service dependency part
            elif son_type == 'service' and father_type == 'service':
                son = son_name.split(',')
                father = father_name.split(',')
                son_svc = arb.conf.services.find_srv_by_name_and_hostname(son[0], son[1])
                father_svc = arb.conf.services.find_srv_by_name_and_hostname(father[0], father[1])
                if son_svc is not None and father_svc is not None:
                    logger.debug("[Hot dependencies] Found! Service %s and %s" % (son_name, father_name))
                    if not son_svc.is_linked_with_service(father_svc):
                        logger.debug("[Hot dependencies] Doing simple link between %s and %s" % (son_svc.get_name(), father_svc.get_name()))
                        # Add a dep link between the son and the father
                        son_svc.add_service_act_dependency(father_svc, ['w', 'c', 'u'], None, True)
                else:
                    logger.debug("[Hot dependencies] Missing one of %s %s" % (son_name, father_name))

    def hook_tick(self, arb):
        now = int(time.time())
        self.tick_external_command()
        logger.debug("[Hot dependencies] Tick tick for hot dependency")
        # If the mapping file changed, we reload it and update our links
        # if we need it
        if self._is_mapping_file_changed():
            logger.debug("[Hot dependencies] The mapping file changed, I update it")
            self._update_mapping()
            additions, removed = self._got_mapping_changes()
            logger.debug("[Hot dependencies] Additions: %s" % additions)
            logger.debug("[Hot dependencies] Remove: %s " % removed)
            for father_k, son_k in additions:
                son_type, son_name = son_k
                father_type, father_name = father_k
                logger.debug("[Hot dependencies] Got new add %s %s %s %s" % (son_type, son_name, father_type, father_name))
                son = arb.conf.hosts.find_by_name(son_name.strip())
                father = arb.conf.hosts.find_by_name(father_name.strip())
                # if we cannot find them in the conf, bypass them
                if son is None or father is None:
                    logger.debug("[Hot dependencies] not find dumbass!")
                    continue
                logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
                if son_type == 'host' and father_type == 'host':
                    # We just raise the external command, arbiter will do the job
                    # to dispatch them
                    extcmd = "[%lu] ADD_SIMPLE_HOST_DEPENDENCY;%s;%s\n" % (now, son_name, father_name)
                    e = ExternalCommand(extcmd)

                    logger.debug('[Hot dependencies] Raising external command: %s' % extcmd)
                    arb.add(e)
            # And now the deletion part
            for father_k, son_k in removed:
                son_type, son_name = son_k
                father_type, father_name = father_k
                logger.debug("[Hot dependencies] Got new del %s %s %s %s" % (son_type, son_name, father_type, father_name))
                son = arb.conf.hosts.find_by_name(son_name.strip())
                father = arb.conf.hosts.find_by_name(father_name.strip())
                # if we cannot find them in the conf, bypass them
                if son is None or father is None:
                    logger.debug("[Hot dependencies] not find dumbass!")
                    continue
                logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
                if son_type == 'host' and father_type == 'host':
                    # We just raise the external command, arbiter will do the job
                    # to dispatch them
                    extcmd = "[%lu] DEL_HOST_DEPENDENCY;%s;%s\n" % (now, son_name, father_name)
                    e = ExternalCommand(extcmd)

                    logger.debug('[Hot dependencies] Raising external command %s' % extcmd)
                    arb.add(e)
<commit_msg>Cleanup and light make up<commit_after>#!/usr/bin/python

# -*- coding: utf-8 -*-

# Copyright (C) 2009-2012:
#    Gabes Jean, naparuba@gmail.com
#    Gerhard Lausser, Gerhard.Lausser@consol.de
#    Gregory Starck, g.starck@gmail.com
#    Hartmut Goebel, h.goebel@goebel-consult.de
#
# This file is part of Shinken.
#
# Shinken is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Shinken is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Shinken.  If not, see <http://www.gnu.org/licenses/>.

# This Class is a plugin for the Shinken Arbiter. It read a json file
# with all links between objects. Update them (create/delete) at the
# launch or at fly

import time
import os
import subprocess
from shinken.log import logger
# Try to load the json (2.5 and higher) or
# the simplejson if failed (python2.4)
try:
    import json
except ImportError:
    # For old Python version, load
    # simple json (it can be hard json?! It's 2 functions guy!)
    try:
        import simplejson as json
    except ImportError:
        logger.error("[Hot dependencies] Error: you need the json or simplejson module for this script")
        raise

from shinken.basemodule import BaseModule
from shinken.external_command import ExternalCommand

properties = {
    'daemons': ['arbiter'],
    'type': 'hot_dependencies',
    'external': False,
    'phases': ['late_configuration'],
    }

# called by the plugin manager to get a broker
def get_instance(plugin):
    logger.info("[Hot dependencies] Get a Hot dependencies module for arbiter with plugin %s" % plugin.get_name())
    mapping_file = getattr(plugin, 'mapping_file', '')
    mapping_command = getattr(plugin, 'mapping_command', '')
    mapping_command_interval = int(getattr(plugin, 'mapping_command_interval', '60'))
    mapping_command_timeout = int(getattr(plugin, 'mapping_command_timeout', '300'))
    in_debug = getattr(plugin, 'debug', '0') == '1'
    instance = Hot_dependencies_arbiter(plugin, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout, in_debug)
    return instance

# Get hosts and/or services dep by launching a command
# or read a flat file as json format that got theses links
class Hot_dependencies_arbiter(BaseModule):
    def __init__(self, modconf, mapping_file, mapping_command, mapping_command_interval, mapping_command_timeout, in_debug=False):
        BaseModule.__init__(self, modconf)
        self.mapping_file = mapping_file
        self.last_update = 0
        self.last_mapping = set()
        self.mapping = set()
        # The external process part
        self.mapping_command = mapping_command
        self.mapping_command_interval = mapping_command_interval
        self.last_cmd_launch = 0
        self.process = None
        self.mapping_command_timeout = mapping_command_timeout
        self.in_debug = in_debug

    # Called by Arbiter to say 'let's prepare yourself guy'
    def init(self):
        logger.info("I open the HOT dependency module")
        # Remember what we add

    def _is_file_existing(self):
        return os.path.exists(self.mapping_file)

    # Look is the mapping filechanged since the last lookup
    def _is_mapping_file_changed(self):
        try:
            last_update = os.path.getmtime(self.mapping_file)
            if last_update > self.last_update:
                self.last_update = last_update
                return True
        except OSError, exp:  # Maybe the file got problem, we bypass here
            logger.error("[Hot dependencies] Error: %s" % str(exp))
        return False

    # Read the mapping file and update our internal mappings
    def _update_mapping(self):
        f = open(self.mapping_file, 'rb')
        mapping = json.loads(f.read())
        f.close()
        self.last_mapping = self.mapping
        # mapping is a list of list, we want a set of tuples
        # because list cannot be hased for a set pass
        self.mapping = set()
        for e in mapping:
            son, father = e
            self.mapping.add((tuple(son), tuple(father)))

    # Maybe the file is updated, but the mapping is the same
    # if not, look at addition and remove objects
    def _got_mapping_changes(self):
        additions = self.mapping - self.last_mapping
        removed = self.last_mapping - self.mapping

        return additions, removed

    # Launch the external command to generate the file
    def _launch_command(self):
        logger.debug("[Hot dependencies] Launching command %s" % self.mapping_command)
        self.last_cmd_launch = int(time.time())

        # windows subprocess do not want us to close fd
        do_close_fd = (not os.name == 'nt')
        try:
            self.process = subprocess.Popen(
                self.mapping_command,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                close_fds=do_close_fd, shell=True)
        except OSError, exp:
            self.error("Fail Launching the command %s: %s" % (self.mapping_command, exp))

    # Look if the command is finished or not
    def _watch_command_finished(self):
        if self.process.poll() is None:
            # Ok, still unfinished, look if in timeout
            now = time.time()
            if (now - self.last_cmd_launch) > self.mapping_command_timeout:
                logger.debug("[Hot dependencies] The external command %s go in timeout!" % self.mapping_command)
        else:
            logger.info("The external command is done!")
            # it's finished! Cool
            (stdoutdata, stderrdata) = self.process.communicate()
            if self.process.returncode != 0:
                logger.debug("[Hot dependencies] The command return in error: %s \n %s" % (stderrdata, stdoutdata))
            self.process = None

    def tick_external_command(self):
        now = int(time.time())

        # If we got no in progress command, look if we should launch a new one
        if self.process is None:
            if now - self.last_cmd_launch > self.mapping_command_interval:
                if self.mapping_command_interval != 0 and self.mapping_command != '':
                    logger.debug("[Hot dependencies] The command launch is too old, launch a new one")
                    self._launch_command()
#            else:
#                print "The last cmd launch is too early", now - self.last_cmd_launch, self.mapping_command_interval
        else:
            # We got one in progress, we should look if it's finished or not
            self._watch_command_finished()

    # Ok, main function that will load dep from a json file
    def hook_late_configuration(self, arb):
        # We will return external commands to the arbiter, so
        # it can just manage it easily and in a generic way
        ext_cmds = []

        # If the file do not exist, we launch the command
        # and we bail out
        if not self._is_file_existing():
            self._launch_command()
            return

        self._is_mapping_file_changed()
        self._update_mapping()
        additions, removed = self._got_mapping_changes()

        for (father_k, son_k) in additions:
            son_type, son_name = son_k
            father_type, father_name = father_k
            logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
            if son_type == 'host' and father_type == 'host':
                son = arb.conf.hosts.find_by_name(son_name)
                father = arb.conf.hosts.find_by_name(father_name)
                if son is not None and father is not None:
                    logger.debug("[Hot dependencies] Found! %s %s" % (son_name, father_name))
                    if not son.is_linked_with_host(father):
                        logger.debug("[Hot dependencies] Doing simple link between %s and %s" % (son.get_name(), father.get_name()))
                        # Add a dep link between the son and the father
                        son.add_host_act_dependency(father, ['w', 'u', 'd'], None, True)
                else:
                    logger.debug("[Hot dependencies] Missing one of %s %s" % (son_name, father_name))
            elif son_type == 'service' and father_type == 'service':
                son = son_name.split(',')
                father = father_name.split(',')
                son_svc = arb.conf.services.find_srv_by_name_and_hostname(son[0], son[1])
                father_svc = arb.conf.services.find_srv_by_name_and_hostname(father[0], father[1])
                if son_svc is not None and father_svc is not None:
                    logger.debug("[Hot dependencies] Found! Service %s and %s" % (son_name, father_name))
                    if not son_svc.is_linked_with_service(father_svc):
                        logger.debug("[Hot dependencies] Doing simple link between %s and %s" % (son_svc.get_name(), father_svc.get_name()))
                        # Add a dep link between the son and the father
                        son_svc.add_service_act_dependency(father_svc, ['w', 'c', 'u'], None, True)
                else:
                    logger.debug("[Hot dependencies] Missing one of %s %s" % (son_name, father_name))

    def hook_tick(self, arb):
        now = int(time.time())
        self.tick_external_command()
        logger.debug("[Hot dependencies] Tick tick for hot dependency")
        # If the mapping file changed, we reload it and update our links
        # if we need it
        if self._is_mapping_file_changed():
            logger.debug("[Hot dependencies] The mapping file changed, I update it")
            self._update_mapping()
            additions, removed = self._got_mapping_changes()
            logger.debug("[Hot dependencies] Additions: %s" % additions)
            logger.debug("[Hot dependencies] Remove: %s " % removed)
            for father_k, son_k in additions:
                son_type, son_name = son_k
                father_type, father_name = father_k
                logger.debug("[Hot dependencies] Got new add %s %s %s %s" % (son_type, son_name, father_type, father_name))
                son = arb.conf.hosts.find_by_name(son_name.strip())
                father = arb.conf.hosts.find_by_name(father_name.strip())
                # if we cannot find them in the conf, bypass them
                if son is None or father is None:
                    logger.debug("[Hot dependencies] not find dumbass!")
                    continue
                logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
                if son_type == 'host' and father_type == 'host':
                    # We just raise the external command, arbiter will do the job
                    # to dispatch them
                    extcmd = "[%lu] ADD_SIMPLE_HOST_DEPENDENCY;%s;%s\n" % (now, son_name, father_name)
                    e = ExternalCommand(extcmd)

                    logger.debug('[Hot dependencies] Raising external command: %s' % extcmd)
                    arb.add(e)
            # And now the deletion part
            for father_k, son_k in removed:
                son_type, son_name = son_k
                father_type, father_name = father_k
                logger.debug("[Hot dependencies] Got new del %s %s %s %s" % (son_type, son_name, father_type, father_name))
                son = arb.conf.hosts.find_by_name(son_name.strip())
                father = arb.conf.hosts.find_by_name(father_name.strip())
                # if we cannot find them in the conf, bypass them
                if son is None or father is None:
                    logger.debug("[Hot dependencies] not find dumbass!")
                    continue
                logger.info("[Hot dependencies] Linked son : %s and its father: %s" % (son_name, father_name))
                if son_type == 'host' and father_type == 'host':
                    # We just raise the external command, arbiter will do the job
                    # to dispatch them
                    extcmd = "[%lu] DEL_HOST_DEPENDENCY;%s;%s\n" % (now, son_name, father_name)
                    e = ExternalCommand(extcmd)

                    logger.debug('[Hot dependencies] Raising external command %s' % extcmd)
                    arb.add(e)
<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

from flask import Blueprint, render_template
import subprocess
import os

admin_controller = Blueprint('admin_controller', 'admin_controller',
                        template_folder='templates')

@admin_controller.route('/admin/exportlanguages')
def exportlanguages():
	subprocess.Popen(['mongoexport', '-d', 'pastedirectory', '-c', 'languages', '-o', 'export.json'])
	with open ("export.json", "r") as myfile:
		return myfile.read()
	<commit_msg>Better solution<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

from flask import Blueprint, render_template
import subprocess
import tempfile
import os

admin_controller = Blueprint('admin_controller', 'admin_controller',
                        template_folder='templates')

@admin_controller.route('/admin/exportlanguages')
def exportlanguages():
	tempfilename = tempfile.TemporaryFile()
	subprocess.Popen(['mongoexport', '-d', 'pastedirectory', '-c', 'languages', '-o', tempfilename])
	with open (tempfilename, "r") as myfile:
		return myfile.read()
	<|endoftext|>