Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 39 additions & 41 deletions dockerplugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def _c(c):
argument is a string, it is assumed to be the container's ID and only the
first 7 digits will be returned. If it's a dictionary, the string returned
is <7-digit ID>/<name>."""
if type(c) == str or type(c) == unicode:
if type(c) == str:
return c[:7]
return '{id}/{name}'.format(id=c['Id'][:7], name=c['Name'])

Expand Down Expand Up @@ -82,29 +82,32 @@ class BlkioStats(Stats):
def read(cls, container, stats, t):
blkio_stats = stats['blkio_stats']
for key, values in blkio_stats.items():
# Block IO stats are reported by block device (with major/minor
# numbers). We need to group and report the stats of each block
# device independently.
device_stats = {}
for value in values:
k = '{key}-{major}-{minor}'.format(key=key,
if values == None:
pass
else:
# Block IO stats are reported by block device (with major/minor
# numbers). We need to group and report the stats of each block
# device independently.
device_stats = {}
for value in values:
k = '{key}-{major}-{minor}'.format(key=key,
major=value['major'],
minor=value['minor'])
if k not in device_stats:
device_stats[k] = []
device_stats[k].append(value['value'])
if k not in device_stats:
device_stats[k] = []
device_stats[k].append(value['value'])

for type_instance, values in device_stats.items():
if len(values) == 5:
cls.emit(container, 'blkio', values,
for type_instance, values in device_stats.items():
if len(values) == 2:
cls.emit(container, 'blkio', values,
type_instance=type_instance, t=t)
elif len(values) == 1:
# For some reason, some fields contains only one value and
# the 'op' field is empty. Need to investigate this
cls.emit(container, 'blkio.single', values,
elif len(values) == 1:
# For some reason, some fields contains only one value and
# the 'op' field is empty. Need to investigate this
cls.emit(container, 'blkio.single', values,
type_instance=key, t=t)
else:
collectd.warning(('Unexpected number of blkio stats for '
else:
collectd.warning(('Unexpected number of blkio stats for '
'container {container}!')
.format(container=_c(container)))

Expand All @@ -115,11 +118,6 @@ def read(cls, container, stats, t):
cpu_stats = stats['cpu_stats']
cpu_usage = cpu_stats['cpu_usage']

percpu = cpu_usage['percpu_usage']
for cpu, value in enumerate(percpu):
cls.emit(container, 'cpu.percpu.usage', [value],
type_instance='cpu%d' % (cpu,), t=t)

items = sorted(cpu_stats['throttling_data'].items())
cls.emit(container, 'cpu.throttling_data', [x[1] for x in items], t=t)

Expand All @@ -137,30 +135,30 @@ def read(cls, container, stats, t):
cpu_delta = cpu_usage['total_usage'] - precpu_usage['total_usage']
system_delta = system_cpu_usage - precpu_stats['system_cpu_usage']
if system_delta > 0 and cpu_delta > 0:
cpu_percent = 100.0 * cpu_delta / system_delta * len(percpu)
cpu_percent = 100.0 * cpu_delta / system_delta
cls.emit(container, "cpu.percent", ["%.2f" % (cpu_percent)], t=t)


class NetworkStats(Stats):
@classmethod
def read(cls, container, stats, t):
items = sorted(stats['network'].items())
cls.emit(container, 'network.usage', [x[1] for x in items], t=t)
items = stats['networks']['eth0']
itemlist = sorted(items.items())
cls.emit(container, 'network.usage', [x[1] for x in itemlist], t=t)


class MemoryStats(Stats):
@classmethod
def read(cls, container, stats, t):
mem_stats = stats['memory_stats']
values = [mem_stats['limit'], mem_stats['max_usage'],
mem_stats['usage']]
values = [mem_stats['limit'],mem_stats['limit'], mem_stats['usage']]
cls.emit(container, 'memory.usage', values, t=t)

for key, value in (mem_stats.get('stats') or {}).items():
cls.emit(container, 'memory.stats', [value],
type_instance=key, t=t)

mem_usage_no_cache = mem_stats['usage'] - mem_stats['stats']['cache']
mem_usage_no_cache = mem_stats['usage'] - mem_stats['stats']['inactive_file']
mem_percent = 100.0 * mem_usage_no_cache / mem_stats['limit']
cls.emit(container, 'memory.percent', ["%.2f" % mem_percent], t=t)

Expand Down Expand Up @@ -210,13 +208,13 @@ def run(self):
if not self._feed:
self._feed = self._client.stats(self._container,
decode=True)
self._stats = self._feed.next()
self._stats = next(self._feed)
else:
self._stats = self._client.stats(self._container,
decode=True, stream=False)
stream=False)
# Reset failure count on successfull read from the stats API.
failures = 0
except Exception, e:
except Exception as e:
collectd.warning('Error reading stats from {container}: {msg}'
.format(container=_c(self._container), msg=e))

Expand Down Expand Up @@ -256,9 +254,9 @@ class DockerPlugin:
DEFAULT_DOCKER_TIMEOUT = 5

# The stats endpoint is only supported by API >= 1.17
MIN_DOCKER_API_VERSION = '1.17'
MIN_DOCKER_API_VERSION = '1.21'

CLASSES = [NetworkStats, BlkioStats, CpuStats, MemoryStats]
CLASSES = [NetworkStats, CpuStats, MemoryStats]

def __init__(self, docker_url=None):
self.docker_url = docker_url or DockerPlugin.DEFAULT_BASE_URL
Expand All @@ -280,7 +278,7 @@ def configure_callback(self, conf):
self.timeout = int(node.values[0])

def init_callback(self):
self.client = docker.Client(
self.client = docker.APIClient(
base_url=self.docker_url,
version=DockerPlugin.MIN_DOCKER_API_VERSION)
self.client.timeout = self.timeout
Expand Down Expand Up @@ -334,7 +332,7 @@ def read_callback(self):
t = stats['read']
for klass in self.CLASSES:
klass.read(container, stats, t)
except Exception, e:
except Exception as e:
collectd.warning(('Error getting stats for container '
'{container}: {msg}')
.format(container=_c(container), msg=e))
Expand All @@ -352,18 +350,18 @@ def dispatch(self):
identifier += '/' + self.type
if getattr(self, 'type_instance', None):
identifier += '-' + self.type_instance
print 'PUTVAL', identifier, \
':'.join(map(str, [int(self.time)] + self.values))
print ('PUTVAL', identifier, \
':'.join(map(str, [int(self.time)] + self.values)))

class ExecCollectd:
def Values(self):
return ExecCollectdValues()

def warning(self, msg):
print 'WARNING:', msg
print ('WARNING:', msg)

def info(self, msg):
print 'INFO:', msg
print ('INFO:', msg)

def register_read(self, docker_plugin):
pass
Expand Down