Merge changes from topic 'k8s-kubectl'

* changes:
  Add NotReady nodes percentage metric
  Add k8s "kubectl get" collectd plugin to extract metrics
diff --git a/collectd/files/collectd_http_check.conf b/collectd/files/collectd_http_check.conf
index 6246e3b..8c3673a 100644
--- a/collectd/files/collectd_http_check.conf
+++ b/collectd/files/collectd_http_check.conf
@@ -7,6 +7,9 @@
   {%- for name, params in plugin.url.iteritems() %}
   ExpectedCode "{{ name }}" "{{ params.expected_code }}"
   Url "{{ name }}" "{{ params.url }}"
+  {%- if params.get('expected_content') %}
+  ExpectedContent "{{ name }}" "{{ params.expected_content|replace('"','\\"') }}"
+  {%- endif %}
   {%- endfor %}
 </Module>
 {%- endif %}
diff --git a/collectd/files/plugin/collectd_base.py b/collectd/files/plugin/collectd_base.py
index 7959e75..6643693 100644
--- a/collectd/files/plugin/collectd_base.py
+++ b/collectd/files/plugin/collectd_base.py
@@ -139,7 +139,7 @@
                 'meta':   {'tagA': 'valA'}}
             {'type': 'dropped_bytes', 'values': [1,2]}
         """
-        raise NotImplemented("Must be implemented by the subclass!")
+        raise NotImplementedError("Must be implemented by the subclass!")
 
     def dispatch_metric(self, metric):
         values = metric['values']
diff --git a/collectd/files/plugin/collectd_openstack.py b/collectd/files/plugin/collectd_openstack.py
index f0ecbfc..84b0092 100644
--- a/collectd/files/plugin/collectd_openstack.py
+++ b/collectd/files/plugin/collectd_openstack.py
@@ -368,20 +368,20 @@
 
                 _objects.extend(bulk_objs)
 
-                links = resp.get('{}_links'.format(object_name))
-                if links is None or self.pagination_limit is None:
-                    # Either the pagination is not supported or there is
-                    # no more data
-                    # In both cases, we got at this stage all the data we
-                    # can have.
+                if self.pagination_limit is None:
                     break
 
-                # if there is no 'next' link in the response, all data has
-                # been read.
-                if len([i for i in links if i.get('rel') == 'next']) == 0:
-                    break
+                links = resp.get('{}_links'.format(object_name), [])
+                # Glance has not <object>_links section but a 'next' item
+                has_next = len(
+                    [i for i in links if i.get('rel') == 'next']) > 0 or \
+                    resp.get('next')
 
-                _opts['marker'] = bulk_objs[-1]['id']
+                if has_next:
+                    _opts['marker'] = bulk_objs[-1]['id']
+                else:
+                    # all data has been read
+                    break
 
             if not has_failure:
                 self._last_run = last_run
diff --git a/collectd/files/plugin/http_check.py b/collectd/files/plugin/http_check.py
index 9009801..1002928 100644
--- a/collectd/files/plugin/http_check.py
+++ b/collectd/files/plugin/http_check.py
@@ -40,6 +40,7 @@
         )
         self.urls = {}
         self.expected_codes = {}
+        self.expected_contents = {}
 
     def config_callback(self, config):
         super(HTTPCheckPlugin, self).config_callback(config)
@@ -48,6 +49,8 @@
                 self.urls[node.values[0]] = node.values[1]
             elif node.key == 'ExpectedCode':
                 self.expected_codes[node.values[0]] = int(node.values[1])
+            elif node.key == 'ExpectedContent':
+                self.expected_contents[node.values[0]] = node.values[1]
 
     def itermetrics(self):
         for name, url in self.urls.items():
@@ -57,7 +60,7 @@
                 self.logger.warning("Got exception for '{}': {}".format(
                     url, e)
                 )
-                yield {'type_instance': name, 'values': self.FAIL}
+                status = self.FAIL
             else:
 
                 expected_code = self.expected_codes.get(name, 200)
@@ -67,11 +70,21 @@
                          "while {} is expected").format(name, url,
                                                         r.status_code,
                                                         expected_code))
-                    yield {'type_instance': name, 'values': self.FAIL}
+                    status = self.FAIL
                 else:
                     self.logger.debug(
                         "Got response from {}: '{}'".format(url, r.content))
-                    yield {'type_instance': name, 'values': self.OK}
+                    status = self.OK
+                    expected_content = self.expected_contents.get(name)
+                    if expected_content:
+                        if r.content != expected_content:
+                            status = self.FAIL
+                            self.logger.warning(
+                                'Content "{}" does not match "{}"'.format(
+                                    r.content[0:30], expected_content
+                                ))
+
+            yield {'type_instance': name, 'values': status }
 
 
 plugin = HTTPCheckPlugin(collectd, disable_check_metric=True)