Add Contrail Python modules

Now using normalized attributes names
diff --git a/collectd/files/plugin/collectd_contrail_apis.py b/collectd/files/plugin/collectd_contrail_apis.py
new file mode 100644
index 0000000..0125a22
--- /dev/null
+++ b/collectd/files/plugin/collectd_contrail_apis.py
@@ -0,0 +1,134 @@
+#!/usr/bin/python
+#
+# Copyright 2016 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import collectd
+import requests
+import xml.dom.minidom
+import xml
+
+import collectd_base as base
+
+
+NAME = 'contrail'
+# Default sampling interval
+INTERVAL = 60
+
+
+def check_state(item, state):
+    return item.getElementsByTagName(
+        "state")[0].childNodes[0].toxml() == state
+
+
+class ContrailApiPlugin(base.Base):
+
+    def __init__(self, *args, **kwargs):
+        super(ContrailApiPlugin, self).__init__(*args, **kwargs)
+        self.plugin = NAME
+        self.session = requests.Session()
+        self.session.mount(
+            'http://',
+            requests.adapters.HTTPAdapter(max_retries=self.max_retries)
+        )
+        self.session.mount(
+            'https://',
+            requests.adapters.HTTPAdapter(max_retries=self.max_retries)
+        )
+        self.urls = {}
+        self.xml_element = {}
+        self.result_type = {}
+        self.state = {}
+
+    def config_callback(self, config):
+        super(ContrailApiPlugin, self).config_callback(config)
+        for node in config.children:
+            self.logger.debug("Got config request for '{}': {} {}".format(
+                node.key.lower(), node.values[0], node.values[1])
+            )
+            if node.key.lower() == "url":
+                self.urls[node.values[0]] = node.values[1]
+            elif node.key.lower() == 'xml_element':
+                self.xml_element[node.values[0]] = node.values[1]
+            elif node.key.lower() == 'result_type':
+                self.result_type[node.values[0]] = node.values[1]
+            elif node.key.lower() == 'state':
+                self.state[node.values[0]] = node.values[1]
+
+    def itermetrics(self):
+        for name, url in self.urls.items():
+            self.logger.debug("Requesting {} URL {}".format(
+                name, url)
+            )
+            try:
+                r = self.session.get(url, timeout=self.timeout)
+            except Exception as e:
+                msg = "Got exception for '{}': {}".format(name, e)
+                raise base.CheckException(msg)
+            else:
+                if r.status_code != 200:
+                    self.logger.error(
+                        ("{} ({}) responded with code {} "
+                         "").format(name, url,
+                                    r.status_code))
+                    yield {'type_instance': name, 'values': self.FAIL}
+                else:
+                    try:
+                        self.logger.debug(
+                            "Got response from {}: '{}'"
+                            "".format(url, r.text))
+                        px = xml.dom.minidom.parseString(r.text)
+                        itemlist = px.getElementsByTagName(
+                            self.xml_element[name]
+                        )
+                        if name not in self.result_type:
+                            count = 0
+                            state = self.state.get('name')
+                            for i in itemlist:
+                                if state is None or check_state(i, state):
+                                    count = count + 1
+                            self.logger.debug(
+                                "Got count for {}: '{}'".format(name, count))
+                            yield {'type_instance': name, 'values': count}
+                        else:
+                            rval = itemlist[0].getElementsByTagName(
+                                self.result_type[name]
+                            )[0].childNodes[0].toxml()
+                        self.logger.debug(
+                            "Got val for {}: '{}'".format(name, rval))
+                        yield {'type_instance': name, 'values': rval}
+                    except Exception as e:
+                        msg = ("Got exception while parsing "
+                               "response for '{}': {}").format(name, e)
+                        raise base.CheckException(msg)
+
+
+plugin = ContrailApiPlugin(collectd)
+
+
+def config_callback(conf):
+    plugin.config_callback(conf)
+
+
+def notification_callback(notification):
+    plugin.notification_callback(notification)
+
+
+def read_callback():
+    plugin.conditional_read_callback()
+
+collectd.register_config(config_callback)
+collectd.register_notification(notification_callback)
+collectd.register_read(read_callback, INTERVAL)
diff --git a/collectd/files/plugin/contrail_ifmap_elements_count.py b/collectd/files/plugin/contrail_ifmap_elements_count.py
new file mode 100644
index 0000000..a9831d2
--- /dev/null
+++ b/collectd/files/plugin/contrail_ifmap_elements_count.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+#
+#    Copyright 2016 Mirantis, Inc.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+
+import time
+import signal
+import string
+import subprocess
+import sys
+
+plugin_name = "contrail"
+plugin_instance = "ifmap-elements-count"
+plugin_interval = 90
+plugin_type = 'gauge'
+
+command = "/usr/bin/ifmap-view visual visual 2>&1 | wc -l"
+
+
+def restore_sigchld():
+    signal.signal(signal.SIGCHLD, signal.SIG_DFL)
+
+
+def log_verbose(msg):
+    collectd.info('%s plugin [verbose]: %s' % (plugin_name, msg))
+
+
+def payload():
+    ifmap_view_number_of_elements = subprocess.check_output(
+        command, shell=True)
+    return ifmap_view_number_of_elements
+
+
+def configure_callback(conf):
+    for node in conf.children:
+        val = str(node.values[0])
+
+
+def payload_callback():
+    value = payload()
+    # log_verbose(
+    #     'Sending value: %s.%s=%s' % (
+    #         plugin_name, '-'.join([val.plugin, val.type]), value))
+    val = collectd.Values(
+        plugin=plugin_name,  # metric source
+        plugin_instance=plugin_instance,
+        type=plugin_type,
+        type_instance=plugin_name,
+        interval=plugin_interval,
+        meta={'0': True},
+        values=[value]
+    )
+
+    val.dispatch()
+
+
+if __name__ == '__main__':
+    print "Plugin: " + plugin_name
+    payload = payload()
+    print("%s" % (payload))
+    sys.exit(0)
+else:
+    import collectd
+
+    collectd.register_init(restore_sigchld)
+    collectd.register_config(configure_callback)
+    collectd.register_read(payload_callback, plugin_interval)