mirror of
https://github.com/nicolargo/glances.git
synced 2024-11-25 19:17:09 +03:00
Docker plugin first version
This commit is contained in:
parent
f2e7c9e880
commit
0d5dcd4f13
3
NEWS
3
NEWS
@ -10,7 +10,8 @@ Version 2.3
|
||||
* Add Statsd export module (--export-statsd) (issue #465)
|
||||
* Refactor export module (CSV export option is now --export-csv). It is now possible to export stats from the Glances client mode (issue #463)
|
||||
* The Web inteface is now based on BootStarp / RWD grid (issue #417, #366 and #461) Thanks to Nicolas Hart @nclsHart
|
||||
* Add the RAID plugins (issue #447)
|
||||
* Add the RAID plugin (issue #447)
|
||||
* Add the Docker plugin (issue #440)
|
||||
|
||||
Version 2.2.1
|
||||
=============
|
||||
|
@ -44,6 +44,7 @@ Optional dependencies:
|
||||
- ``influxdb`` (for the InfluxDB export module)
|
||||
- ``statsd`` (for the StatsD export module)
|
||||
- ``pystache`` (for the action script feature)
|
||||
- ``docker-py`` (for the Docker monitoring support) [Linux-only]
|
||||
|
||||
Installation
|
||||
============
|
||||
|
@ -671,6 +671,13 @@ Each alert message displays the following information:
|
||||
4. {min,avg,max} values or number of running processes for monitored
|
||||
processes list alerts
|
||||
|
||||
Docker
|
||||
------
|
||||
|
||||
If you use Docker, Glances can help you to monitor your container. Glances uses the Docker API through the Docker-Py library.
|
||||
|
||||
.. image:: images/docker.png
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
|
BIN
docs/images/docker.png
Normal file
BIN
docs/images/docker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
@ -77,6 +77,8 @@ class GlancesMain(object):
|
||||
dest='disable_sensors', help=_('disable sensors module'))
|
||||
parser.add_argument('--disable-raid', action='store_true', default=False,
|
||||
dest='disable_raid', help=_('disable RAID module'))
|
||||
parser.add_argument('--disable-docker', action='store_true', default=False,
|
||||
dest='disable_docker', help=_('disable Docker module'))
|
||||
parser.add_argument('--disable-left-sidebar', action='store_true', default=False,
|
||||
dest='disable_left_sidebar', help=_('disable network, disk io, FS and sensors modules'))
|
||||
parser.add_argument('--disable-process', action='store_true', default=False,
|
||||
|
@ -266,6 +266,9 @@ class _GlancesCurses(object):
|
||||
elif self.pressedkey == ord('d'):
|
||||
# 'd' > Show/hide disk I/O stats
|
||||
self.args.disable_diskio = not self.args.disable_diskio
|
||||
elif self.pressedkey == ord('D'):
|
||||
# 'D' > Show/hide Docker stats
|
||||
self.args.disable_docker = not self.args.disable_docker
|
||||
elif self.pressedkey == ord('e'):
|
||||
# 'e' > Enable/Disable extended stats for top process
|
||||
self.args.enable_process_extended = not self.args.enable_process_extended
|
||||
@ -432,6 +435,8 @@ class _GlancesCurses(object):
|
||||
stats_sensors = stats.get_plugin(
|
||||
'sensors').get_stats_display(args=self.args)
|
||||
stats_now = stats.get_plugin('now').get_stats_display()
|
||||
stats_docker = stats.get_plugin('docker').get_stats_display(
|
||||
args=self.args)
|
||||
stats_processcount = stats.get_plugin(
|
||||
'processcount').get_stats_display(args=self.args)
|
||||
stats_monitor = stats.get_plugin(
|
||||
@ -441,7 +446,8 @@ class _GlancesCurses(object):
|
||||
|
||||
# Adapt number of processes to the available space
|
||||
max_processes_displayed = screen_y - 11 - \
|
||||
self.get_stats_display_height(stats_alert)
|
||||
self.get_stats_display_height(stats_alert) - \
|
||||
self.get_stats_display_height(stats_docker)
|
||||
if self.args.enable_process_extended and not self.args.process_tree:
|
||||
max_processes_displayed -= 4
|
||||
if max_processes_displayed < 0:
|
||||
@ -534,9 +540,11 @@ class _GlancesCurses(object):
|
||||
self.next_line = self.saved_line
|
||||
|
||||
# Display right sidebar
|
||||
# (PROCESS_COUNT+MONITORED+PROCESS_LIST+ALERT)
|
||||
# ((DOCKER)+PROCESS_COUNT+(MONITORED)+PROCESS_LIST+ALERT)
|
||||
self.new_column()
|
||||
self.new_line()
|
||||
self.display_plugin(stats_docker)
|
||||
self.new_line()
|
||||
self.display_plugin(stats_processcount)
|
||||
if glances_processes.get_process_filter() is None and cs_status == 'None':
|
||||
# Do not display stats monitor list if a filter exist
|
||||
|
@ -56,6 +56,7 @@ class Plugin(GlancesPlugin):
|
||||
"""Reset/init the stats."""
|
||||
self.stats = []
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update battery capacity stats using the input method."""
|
||||
# Reset stats
|
||||
|
201
glances/plugins/glances_docker.py
Normal file
201
glances/plugins/glances_docker.py
Normal file
@ -0,0 +1,201 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# This file is part of Glances.
|
||||
#
|
||||
# Copyright (C) 2015 Nicolargo <nicolas@nicolargo.com>
|
||||
#
|
||||
# Glances is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Glances 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Docker plugin."""
|
||||
|
||||
# Import Glances libs
|
||||
from glances.core.glances_logging import logger
|
||||
from glances.plugins.glances_plugin import GlancesPlugin
|
||||
|
||||
# Docker-py library (optional and Linux-only)
|
||||
# https://github.com/docker/docker-py
|
||||
try:
|
||||
import docker
|
||||
import requests
|
||||
except ImportError as e:
|
||||
logger.debug("Docker library not found (%s). Glances cannot grab Docker info." % e)
|
||||
docker_tag = False
|
||||
else:
|
||||
docker_tag = True
|
||||
|
||||
|
||||
class Plugin(GlancesPlugin):
|
||||
|
||||
"""Glances' Docker plugin.
|
||||
|
||||
stats is a list
|
||||
"""
|
||||
|
||||
def __init__(self, args=None):
|
||||
"""Init the plugin."""
|
||||
GlancesPlugin.__init__(self, args=args)
|
||||
|
||||
# The plgin can be disable using: args.disable_docker
|
||||
self.args = args
|
||||
|
||||
# We want to display the stat in the curse interface
|
||||
self.display_curse = True
|
||||
|
||||
# Init the Docker API
|
||||
self.docker_client = self.connect()
|
||||
if self.docker_client is None:
|
||||
global docker_tag
|
||||
docker_tag = False
|
||||
|
||||
def connect(self, version=None):
|
||||
"""Connect to the Docker server"""
|
||||
# Init connection to the Docker API
|
||||
if version is None:
|
||||
ret = docker.Client(base_url='unix://var/run/docker.sock')
|
||||
else:
|
||||
ret = docker.Client(base_url='unix://var/run/docker.sock',
|
||||
version=version)
|
||||
try:
|
||||
ret.version()
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
# Connexion error (Docker not detected)
|
||||
# Let this message in debug mode
|
||||
logger.debug("Can't connect to the Docker server (%s)" % e)
|
||||
ret = None
|
||||
except docker.errors.APIError as e:
|
||||
if version is None:
|
||||
# API error (Version mismatch ?)
|
||||
logger.debug("Docker API error (%s)" % e)
|
||||
# Try the connection with the server version
|
||||
import re
|
||||
version = re.search('server\:\ (.*)\)\"\)', str(e))
|
||||
if version:
|
||||
logger.debug("Try connection with Docker API version %s" % version.group(1))
|
||||
ret = self.connect(version=version.group(1))
|
||||
else:
|
||||
# API error
|
||||
logger.error("Docker API error (%s)" % e)
|
||||
ret = None
|
||||
except Exception as e:
|
||||
# Others exceptions...
|
||||
# Connexion error (Docker not detected)
|
||||
logger.error("Can't connect to the Docker server (%s)" % e)
|
||||
ret = None
|
||||
|
||||
return ret
|
||||
|
||||
def reset(self):
|
||||
"""Reset/init the stats."""
|
||||
self.stats = {}
|
||||
|
||||
@GlancesPlugin._log_result_decorator
|
||||
def update(self):
|
||||
"""Update Docker stats using the input method.
|
||||
"""
|
||||
# Reset stats
|
||||
self.reset()
|
||||
|
||||
# The Docker-py lib is mandatory
|
||||
if not docker_tag or self.args.disable_docker:
|
||||
return self.stats
|
||||
|
||||
if self.get_input() == 'local':
|
||||
# Update stats
|
||||
# Exemple: {
|
||||
# "KernelVersion": "3.16.4-tinycore64",
|
||||
# "Arch": "amd64",
|
||||
# "ApiVersion": "1.15",
|
||||
# "Version": "1.3.0",
|
||||
# "GitCommit": "c78088f",
|
||||
# "Os": "linux",
|
||||
# "GoVersion": "go1.3.3"
|
||||
# }
|
||||
self.stats['version'] = self.docker_client.version()
|
||||
# Example: [{u'Status': u'Up 36 seconds',
|
||||
# u'Created': 1420378904,
|
||||
# u'Image': u'nginx:1',
|
||||
# u'Ports': [{u'Type': u'tcp', u'PrivatePort': 443},
|
||||
# {u'IP': u'0.0.0.0', u'Type': u'tcp', u'PublicPort': 8080, u'PrivatePort': 80}],
|
||||
# u'Command': u"nginx -g 'daemon off;'",
|
||||
# u'Names': [u'/webstack_nginx_1'],
|
||||
# u'Id': u'b0da859e84eb4019cf1d965b15e9323006e510352c402d2f442ea632d61faaa5'}]
|
||||
self.stats['containers'] = self.docker_client.containers()
|
||||
|
||||
elif self.get_input() == 'snmp':
|
||||
# Update stats using SNMP
|
||||
# Not available
|
||||
pass
|
||||
|
||||
return self.stats
|
||||
|
||||
def msg_curse(self, args=None):
|
||||
"""Return the dict to display in the curse interface."""
|
||||
# Init the return message
|
||||
ret = []
|
||||
|
||||
# Only process if stats exist (and non null) and display plugin enable...
|
||||
if self.stats == {} or args.disable_docker or len(self.stats['containers']) == 0:
|
||||
return ret
|
||||
|
||||
# Build the string message
|
||||
# Title
|
||||
msg = '{0}'.format(_("CONTAINERS"))
|
||||
ret.append(self.curse_add_line(msg, "TITLE"))
|
||||
msg = ' {0}'.format(len(self.stats['containers']))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' ({0} {1})'.format(_("served by Docker"),
|
||||
self.stats['version']["Version"])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
# Header
|
||||
ret.append(self.curse_new_line())
|
||||
msg = '{0:>14}'.format(_("Id"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' {0:20}'.format(_("Name"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = '{0:>26}'.format(_("Status"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
msg = ' {0:8}'.format(_("Command"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Data
|
||||
for container in self.stats['containers']:
|
||||
ret.append(self.curse_new_line())
|
||||
# Id
|
||||
msg = '{0:>14}'.format(container['Id'][0:12])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Name
|
||||
name = container['Names'][0]
|
||||
if len(name) > 20:
|
||||
name = '_' + name[:-19]
|
||||
else:
|
||||
name[0:20]
|
||||
msg = ' {0:20}'.format(name)
|
||||
ret.append(self.curse_add_line(msg))
|
||||
# Status
|
||||
status = self.container_alert(container['Status'])
|
||||
msg = container['Status'].replace("minute", "min")
|
||||
msg = '{0:>26}'.format(msg[0:25])
|
||||
ret.append(self.curse_add_line(msg, status))
|
||||
# Command
|
||||
msg = ' {0}'.format(container['Command'])
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
return ret
|
||||
|
||||
def container_alert(self, status):
|
||||
"""Analyse the container status"""
|
||||
if "Paused" in status:
|
||||
return 'CAREFUL'
|
||||
else:
|
||||
return 'OK'
|
@ -139,6 +139,10 @@ class Plugin(GlancesPlugin):
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("/", _("Enable/disable short processes name"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
ret.append(self.curse_new_line())
|
||||
msg = msg_col.format("D", _("Enable/disable Docker stats"))
|
||||
ret.append(self.curse_add_line(msg))
|
||||
|
||||
|
||||
ret.append(self.curse_new_line())
|
||||
ret.append(self.curse_new_line())
|
||||
|
Loading…
Reference in New Issue
Block a user