From 3cab5e4d84acd75a72d2247ab7c8f143c749037f Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Wed, 28 Nov 2012 23:12:07 +0000 Subject: [PATCH] Add graphite. Change-Id: I276641d55e966cd76013cae847061c3ac7996864 Reviewed-on: https://review.openstack.org/17094 Reviewed-by: Clark Boylan Approved: Monty Taylor Reviewed-by: Monty Taylor Tested-by: Jenkins --- manifests/site.pp | 10 + modules/graphite/files/carbon-cache.init | 99 +++++++ modules/graphite/files/graphite-init-db.py | 40 +++ modules/graphite/files/statsd.default | 2 + modules/graphite/files/statsd.init | 157 ++++++++++ modules/graphite/manifests/init.pp | 234 +++++++++++++++ modules/graphite/templates/admin.ini | 4 + modules/graphite/templates/carbon.conf.erb | 280 ++++++++++++++++++ modules/graphite/templates/config.js.erb | 57 ++++ modules/graphite/templates/graphite.vhost.erb | 56 ++++ modules/graphite/templates/graphite.wsgi.erb | 16 + .../graphite/templates/local_settings.py.erb | 3 + .../templates/storage-schemas.conf.erb | 16 + .../openstack_project/manifests/graphite.pp | 26 ++ 14 files changed, 1000 insertions(+) create mode 100644 modules/graphite/files/carbon-cache.init create mode 100644 modules/graphite/files/graphite-init-db.py create mode 100644 modules/graphite/files/statsd.default create mode 100644 modules/graphite/files/statsd.init create mode 100644 modules/graphite/manifests/init.pp create mode 100644 modules/graphite/templates/admin.ini create mode 100644 modules/graphite/templates/carbon.conf.erb create mode 100644 modules/graphite/templates/config.js.erb create mode 100644 modules/graphite/templates/graphite.vhost.erb create mode 100644 modules/graphite/templates/graphite.wsgi.erb create mode 100644 modules/graphite/templates/local_settings.py.erb create mode 100644 modules/graphite/templates/storage-schemas.conf.erb create mode 100644 modules/openstack_project/manifests/graphite.pp diff --git a/manifests/site.pp b/manifests/site.pp index 7cdc620006..112affb794 100644 --- a/manifests/site.pp +++ b/manifests/site.pp @@ -105,6 +105,16 @@ node 'ci-puppetmaster.openstack.org' { } } +node 'graphite.openstack.org' { + class { 'openstack_project::graphite': + sysadmins => hiera('sysadmins'), + graphite_admin_user => hiera('graphite_admin_user'), + graphite_admin_email => hiera('graphite_admin_email'), + graphite_admin_password => hiera('graphite_admin_password'), + statsd_hosts => ['jenkins.openstack.org'], + } +} + node 'groups.openstack.org' { class { 'openstack_project::groups': sysadmins => hiera('sysadmins'), diff --git a/modules/graphite/files/carbon-cache.init b/modules/graphite/files/carbon-cache.init new file mode 100644 index 0000000000..54fe98bf88 --- /dev/null +++ b/modules/graphite/files/carbon-cache.init @@ -0,0 +1,99 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: carbon-cache +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Carbon Cache +# Description: Carbon Cache +### END INIT INFO + +CARBON_DAEMON="cache" +NAME="carbon-${CARBON_DAEMON}" + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +INSTANCES=`grep "^\[${CARBON_DAEMON}" /etc/graphite/carbon.conf | cut -d \[ -f 2 | cut -d \] -f 1 | cut -d : -f 2` + +function die { + echo $1 + exit 1 +} + +start(){ + for INSTANCE in ${INSTANCES}; do + if [ "${INSTANCE}" == "${CARBON_DAEMON}" ]; then + INSTANCE="a"; + fi; + echo "Starting carbon-${CARBON_DAEMON}:${INSTANCE}..." + /usr/local/bin/carbon-${CARBON_DAEMON}.py --config /etc/graphite/carbon.conf --instance=${INSTANCE} start; + + if [ $? -eq 0 ]; then + echo "success" + else + echo "failure" + fi; + echo "" + done; +} + +stop(){ + for INSTANCE in ${INSTANCES}; do + if [ "${INSTANCE}" == "${CARBON_DAEMON}" ]; then + INSTANCE="a"; + fi; + echo "Stopping carbon-${CARBON_DAEMON}:${INSTANCE}..." + /usr/local/bin/carbon-${CARBON_DAEMON}.py --config /etc/graphite/carbon.conf --instance=${INSTANCE} stop + + if [ `sleep 3; /usr/bin/pgrep -f "carbon-${CARBON_DAEMON}.py --instance=${INSTANCE}" | /usr/bin/wc -l` -gt 0 ]; then + echo "Carbon did not stop yet. Sleeping longer, then force killing it..."; + sleep 20; + /usr/bin/pkill -9 -f "carbon-${CARBON_DAEMON}.py --instance=${INSTANCE}"; + fi; + + if [ $? -eq 0 ]; then + echo "success" + else + echo "failure" + fi; + echo "" + done; +} + +status(){ + for INSTANCE in ${INSTANCES}; do + if [ "${INSTANCE}" == "${CARBON_DAEMON}" ]; then + INSTANCE="a"; + fi; + /usr/local/bin/carbon-${CARBON_DAEMON}.py --config /etc/graphite/carbon.conf --instance=${INSTANCE} status; + done; +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + status) + status + ;; + restart|reload) + stop + start + ;; + *) + echo $"Usage: $0 {start|stop|restart|status}" + exit 1 +esac + diff --git a/modules/graphite/files/graphite-init-db.py b/modules/graphite/files/graphite-init-db.py new file mode 100644 index 0000000000..1a83632b4b --- /dev/null +++ b/modules/graphite/files/graphite-init-db.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# Copyright 2012 Hewlett-Packard Development Company, L.P. +# +# 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 os +# Must happen before django imports +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "graphite.settings") + +import sys +import ConfigParser + +from django.core import management +from django.contrib.auth import models as auth_models + +config = ConfigParser.ConfigParser() +config.read(os.path.expanduser(sys.argv[1])) + +USER = config.get('admin', 'user') +EMAIL = config.get('admin', 'email') +PASS = config.get('admin', 'password') + +management.call_command('syncdb', interactive=False) + +try: + auth_models.User.objects.get(username=USER) + print 'Admin user already exists.' +except auth_models.User.DoesNotExist: + print 'Creating admin user' + auth_models.User.objects.create_superuser(USER, EMAIL, PASS) diff --git a/modules/graphite/files/statsd.default b/modules/graphite/files/statsd.default new file mode 100644 index 0000000000..fd509c58f3 --- /dev/null +++ b/modules/graphite/files/statsd.default @@ -0,0 +1,2 @@ +DAEMON_ARGS="/opt/statsd/stats.js /etc/statsd/config.js" +CHDIR="/opt/statsd" diff --git a/modules/graphite/files/statsd.init b/modules/graphite/files/statsd.init new file mode 100644 index 0000000000..7a9dda24ab --- /dev/null +++ b/modules/graphite/files/statsd.init @@ -0,0 +1,157 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: statsd +# Required-Start: $network $local_fs +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +### END INIT INFO + +# Do NOT "set -e" + +PATH=$PATH:/usr/local/bin:/usr/bin:/bin +NODE_BIN=$(which nodejs||which node) + +if [ ! -x "$NODE_BIN" ]; then + echo "Can't find executable nodejs or node in PATH=$PATH" + exit 1 +fi + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="StatsD" +NAME=statsd +DAEMON=$NODE_BIN +DAEMON_ARGS="/usr/share/statsd/stats.js /etc/statsd/localConfig.js 2>&1 >> /var/log/statsd/statsd.log " +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +CHDIR="/usr/share/statsd" +USER=statsd + +# Exit if the package is not installed +# [ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet -m --pidfile $PIDFILE -c $USER --startas $DAEMON --background --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet -m --pidfile $PIDFILE -c $USER --startas $DAEMON --background --chdir $CHDIR -- \ + $DAEMON_ARGS > /dev/null 2> /var/log/$NAME-stderr.log \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=0/0/KILL/5 --pidfile $PIDFILE + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/modules/graphite/manifests/init.pp b/modules/graphite/manifests/init.pp new file mode 100644 index 0000000000..413eebb166 --- /dev/null +++ b/modules/graphite/manifests/init.pp @@ -0,0 +1,234 @@ +# == Class: graphite +# +class graphite( + $vhost_name = $::fqdn, + $graphite_admin_user = '', + $graphite_admin_email = '', + $graphite_admin_password = '', +) { + $packages = [ 'python-django', + 'python-django-tagging', + 'python-cairo', + 'nodejs' ] + + include apache + include pip + + package { $packages: + ensure => present, + } + + vcsrepo { '/opt/graphite-web': + ensure => latest, + provider => git, + revision => '0.9.x', + source => 'https://github.com/graphite-project/graphite-web.git', + } + + exec { 'install_graphite_web' : + command => 'python setup.py install --install-scripts=/usr/local/bin --install-lib=/usr/local/lib/python2.7/dist-packages --install-data=/var/lib/graphite', + cwd => '/opt/graphite-web', + path => '/bin:/usr/bin', + refreshonly => true, + subscribe => Vcsrepo['/opt/graphite-web'], + require => Exec['install_carbon'], + } + + vcsrepo { '/opt/carbon': + ensure => latest, + provider => git, + revision => '0.9.x', + source => 'https://github.com/graphite-project/carbon.git', + } + + exec { 'install_carbon' : + command => 'python setup.py install --install-scripts=/usr/local/bin --install-lib=/usr/local/lib/python2.7/dist-packages --install-data=/var/lib/graphite', + cwd => '/opt/carbon', + path => '/bin:/usr/bin', + refreshonly => true, + subscribe => Vcsrepo['/opt/carbon'], + require => Exec['install_whisper'], + } + + vcsrepo { '/opt/whisper': + ensure => latest, + provider => git, + revision => '0.9.x', + source => 'https://github.com/graphite-project/whisper.git', + } + + exec { 'install_whisper' : + command => 'python setup.py install', + cwd => '/opt/whisper', + path => '/bin:/usr/bin', + refreshonly => true, + subscribe => Vcsrepo['/opt/whisper'], + } + + user { 'statsd': + ensure => present, + home => '/home/statsd', + shell => '/bin/bash', + gid => 'statsd', + managehome => true, + require => Group['statsd'], + } + + group { 'statsd': + ensure => present, + } + + file { '/var/lib/graphite': + ensure => directory, + owner => 'www-data', + group => 'www-data', + require => Package['apache2'], + } + + file { '/var/log/graphite': + ensure => directory, + owner => 'www-data', + group => 'www-data', + require => Package['apache2'], + } + + file { '/etc/graphite': + ensure => directory, + } + + exec { 'graphite_sync_db': + user => 'www-data', + command => 'python /usr/local/bin/graphite-init-db.py /etc/graphite/admin.ini', + cwd => '/usr/local/lib/python2.7/dist-packages/graphite', + path => '/bin:/usr/bin', + onlyif => 'test ! -f /var/lib/graphite/graphite.db', + require => [ Exec['install_graphite_web'], + File['/var/lib/graphite'], + Package['apache2'], + File['/usr/local/lib/python2.7/dist-packages/graphite/local_settings.py'], + File['/usr/local/bin/graphite-init-db.py'], + File['/etc/graphite/admin.ini']], + } + + apache::vhost { $vhost_name: + port => 80, + priority => '50', + docroot => '/var/lib/graphite/webapp', + template => 'graphite/graphite.vhost.erb', + } + + vcsrepo { '/opt/statsd': + ensure => latest, + provider => git, + source => 'https://github.com/etsy/statsd.git', + } + + file { '/etc/statsd': + ensure => directory, + } + + file { '/etc/statsd/config.js': + owner => 'statsd', + group => 'statsd', + mode => '0444', + content => template('graphite/config.js.erb'), + require => File['/etc/statsd'], + } + + file { '/etc/graphite/carbon.conf': + mode => '0444', + content => template('graphite/carbon.conf.erb'), + require => File['/etc/graphite'], + } + + file { '/etc/graphite/graphite.wsgi': + mode => '0444', + content => template('graphite/graphite.wsgi.erb'), + require => File['/etc/graphite'], + } + + file { '/etc/graphite/storage-schemas.conf': + mode => '0444', + content => template('graphite/storage-schemas.conf.erb'), + require => File['/etc/graphite'], + } + + file { '/usr/local/lib/python2.7/dist-packages/graphite/local_settings.py': + mode => '0444', + content => template('graphite/local_settings.py.erb'), + require => Exec['install_graphite_web'], + } + + file { '/usr/local/bin/graphite-init-db.py': + mode => '0555', + source => 'puppet:///modules/graphite/graphite-init-db.py' + } + + file { '/etc/graphite/admin.ini': + mode => '0400', + owner => 'www-data', + group => 'www-data', + content => template('graphite/admin.ini'), + require => [ File['/etc/graphite'], + Package['apache2']], + } + + file { '/etc/init.d/carbon-cache': + mode => '0555', + source => 'puppet:///modules/graphite/carbon-cache.init' + } + + file { '/etc/init.d/statsd': + mode => '0555', + source => 'puppet:///modules/graphite/statsd.init' + } + + file { '/etc/default/statsd': + mode => '0444', + source => 'puppet:///modules/graphite/statsd.default' + } + + file { ['/etc/rc0.d/K10carbon-cache', + '/etc/rc1.d/K10carbon-cache', + '/etc/rc2.d/S90carbon-cache', + '/etc/rc3.d/S90carbon-cache', + '/etc/rc4.d/S90carbon-cache', + '/etc/rc5.d/S90carbon-cache', + '/etc/rc6.d/K10carbon-cache']: + ensure => link, + target => '/etc/init.d/carbon-cache', + require => File['/etc/init.d/carbon-cache'], + } + + exec { 'carbon-cache-start': + command => '/etc/init.d/carbon-cache start', + require => [File['/etc/init.d/carbon-cache'], + File['/etc/graphite/carbon.conf'], + Exec['install_carbon']], + subscribe => File['/etc/init.d/carbon-cache'], + refreshonly => true, + } + + file { ['/etc/rc0.d/K10statsd', + '/etc/rc1.d/K10statsd', + '/etc/rc2.d/S90statsd', + '/etc/rc3.d/S90statsd', + '/etc/rc4.d/S90statsd', + '/etc/rc5.d/S90statsd', + '/etc/rc6.d/K10statsd']: + ensure => link, + target => '/etc/init.d/statsd', + require => File['/etc/init.d/statsd'], + } + + exec { 'statsd-start': + command => '/etc/init.d/statsd start', + require => [File['/etc/init.d/statsd'], + File['/etc/statsd/config.js'], + Vcsrepo['/opt/statsd']], + subscribe => File['/etc/init.d/statsd'], + refreshonly => true, + } + +} + diff --git a/modules/graphite/templates/admin.ini b/modules/graphite/templates/admin.ini new file mode 100644 index 0000000000..a5c7ffd965 --- /dev/null +++ b/modules/graphite/templates/admin.ini @@ -0,0 +1,4 @@ +[admin] +user=<%= graphite_admin_user %> +email=<%= graphite_admin_email %> +password=<%= graphite_admin_password %> diff --git a/modules/graphite/templates/carbon.conf.erb b/modules/graphite/templates/carbon.conf.erb new file mode 100644 index 0000000000..f9540b98b2 --- /dev/null +++ b/modules/graphite/templates/carbon.conf.erb @@ -0,0 +1,280 @@ +[cache] +# Configure carbon directories. +# +# OS environment variables can be used to tell carbon where graphite is +# installed, where to read configuration from and where to write data. +# +# GRAPHITE_ROOT - Root directory of the graphite installation. +# Defaults to ../ +# GRAPHITE_CONF_DIR - Configuration directory (where this file lives). +# Defaults to $GRAPHITE_ROOT/conf/ +# GRAPHITE_STORAGE_DIR - Storage directory for whipser/rrd/log/pid files. +# Defaults to $GRAPHITE_ROOT/storage/ +# +# To change other directory paths, add settings to this file. The following +# configuration variables are available with these default values: +# +# STORAGE_DIR = $GRAPHITE_STORAGE_DIR +# LOCAL_DATA_DIR = STORAGE_DIR/whisper/ +# WHITELISTS_DIR = STORAGE_DIR/lists/ +# CONF_DIR = STORAGE_DIR/conf/ +# LOG_DIR = STORAGE_DIR/log/ +# PID_DIR = STORAGE_DIR/ +# +# For FHS style directory structures, use: +# +STORAGE_DIR = /var/lib/graphite/ +CONF_DIR = /etc/graphite/ +LOG_DIR = /var/log/graphite/ +PID_DIR = /var/run/ +# +#LOCAL_DATA_DIR = /opt/graphite/storage/whisper/ + +# Specify the user to drop privileges to +# If this is blank carbon runs as the user that invokes it +# This user must have write access to the local data directory +USER = www-data + +# Limit the size of the cache to avoid swapping or becoming CPU bound. +# Sorts and serving cache queries gets more expensive as the cache grows. +# Use the value "inf" (infinity) for an unlimited cache size. +MAX_CACHE_SIZE = inf + +# Limits the number of whisper update_many() calls per second, which effectively +# means the number of write requests sent to the disk. This is intended to +# prevent over-utilizing the disk and thus starving the rest of the system. +# When the rate of required updates exceeds this, then carbon's caching will +# take effect and increase the overall throughput accordingly. +MAX_UPDATES_PER_SECOND = 500 + +# Softly limits the number of whisper files that get created each minute. +# Setting this value low (like at 50) is a good way to ensure your graphite +# system will not be adversely impacted when a bunch of new metrics are +# sent to it. The trade off is that it will take much longer for those metrics' +# database files to all get created and thus longer until the data becomes usable. +# Setting this value high (like "inf" for infinity) will cause graphite to create +# the files quickly but at the risk of slowing I/O down considerably for a while. +MAX_CREATES_PER_MINUTE = 50 + +LINE_RECEIVER_INTERFACE = 0.0.0.0 +LINE_RECEIVER_PORT = 2003 + +# Set this to True to enable the UDP listener. By default this is off +# because it is very common to run multiple carbon daemons and managing +# another (rarely used) port for every carbon instance is not fun. +ENABLE_UDP_LISTENER = False +UDP_RECEIVER_INTERFACE = 0.0.0.0 +UDP_RECEIVER_PORT = 2003 + +PICKLE_RECEIVER_INTERFACE = 0.0.0.0 +PICKLE_RECEIVER_PORT = 2004 + +# Per security concerns outlined in Bug #817247 the pickle receiver +# will use a more secure and slightly less efficient unpickler. +# Set this to True to revert to the old-fashioned insecure unpickler. +USE_INSECURE_UNPICKLER = False + +CACHE_QUERY_INTERFACE = 0.0.0.0 +CACHE_QUERY_PORT = 7002 + +# Set this to False to drop datapoints received after the cache +# reaches MAX_CACHE_SIZE. If this is True (the default) then sockets +# over which metrics are received will temporarily stop accepting +# data until the cache size falls below 95% MAX_CACHE_SIZE. +USE_FLOW_CONTROL = True + +# By default, carbon-cache will log every whisper update. This can be excessive and +# degrade performance if logging on the same volume as the whisper data is stored. +LOG_UPDATES = False + +# On some systems it is desirable for whisper to write synchronously. +# Set this option to True if you'd like to try this. Basically it will +# shift the onus of buffering writes from the kernel into carbon's cache. +WHISPER_AUTOFLUSH = False + +# By default new Whisper files are created pre-allocated with the data region +# filled with zeros to prevent fragmentation and speed up contiguous reads and +# writes (which are common). Enabling this option will cause Whisper to create +# the file sparsely instead. Enabling this option may allow a large increase of +# MAX_CREATES_PER_MINUTE but may have longer term performance implications +# depending on the underlying storage configuration. +# WHISPER_SPARSE_CREATE = False + +# Enabling this option will cause Whisper to lock each Whisper file it writes +# to with an exclusive lock (LOCK_EX, see: man 2 flock). This is useful when +# multiple carbon-cache daemons are writing to the same files +# WHISPER_LOCK_WRITES = False + +# Set this to True to enable whitelisting and blacklisting of metrics in +# CONF_DIR/whitelist and CONF_DIR/blacklist. If the whitelist is missing or +# empty, all metrics will pass through +# USE_WHITELIST = False + +# By default, carbon itself will log statistics (such as a count, +# metricsReceived) with the top level prefix of 'carbon' at an interval of 60 +# seconds. Set CARBON_METRIC_INTERVAL to 0 to disable instrumentation +# CARBON_METRIC_PREFIX = carbon +# CARBON_METRIC_INTERVAL = 60 + +# Enable AMQP if you want to receve metrics using an amqp broker +# ENABLE_AMQP = False + +# Verbose means a line will be logged for every metric received +# useful for testing +# AMQP_VERBOSE = False + +# AMQP_HOST = localhost +# AMQP_PORT = 5672 +# AMQP_VHOST = / +# AMQP_USER = guest +# AMQP_PASSWORD = guest +# AMQP_EXCHANGE = graphite +# AMQP_METRIC_NAME_IN_BODY = False + +# The manhole interface allows you to SSH into the carbon daemon +# and get a python interpreter. BE CAREFUL WITH THIS! If you do +# something like time.sleep() in the interpreter, the whole process +# will sleep! This is *extremely* helpful in debugging, assuming +# you are familiar with the code. If you are not, please don't +# mess with this, you are asking for trouble :) +# +# ENABLE_MANHOLE = False +# MANHOLE_INTERFACE = 127.0.0.1 +# MANHOLE_PORT = 7222 +# MANHOLE_USER = admin +# MANHOLE_PUBLIC_KEY = ssh-rsa AAAAB3NzaC1yc2EAAAABiwAaAIEAoxN0sv/e4eZCPpi3N3KYvyzRaBaMeS2RsOQ/cDuKv11dlNzVeiyc3RFmCv5Rjwn/lQ79y0zyHxw67qLyhQ/kDzINc4cY41ivuQXm2tPmgvexdrBv5nsfEpjs3gLZfJnyvlcVyWK/lId8WUvEWSWHTzsbtmXAF2raJMdgLTbQ8wE= + +# Patterns for all of the metrics this machine will store. Read more at +# http://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol#Bindings +# +# Example: store all sales, linux servers, and utilization metrics +# BIND_PATTERNS = sales.#, servers.linux.#, #.utilization +# +# Example: store everything +# BIND_PATTERNS = # + +# To configure special settings for the carbon-cache instance 'b', uncomment this: +#[cache:b] +#LINE_RECEIVER_PORT = 2103 +#PICKLE_RECEIVER_PORT = 2104 +#CACHE_QUERY_PORT = 7102 +# and any other settings you want to customize, defaults are inherited +# from [carbon] section. +# You can then specify the --instance=b option to manage this instance + + + +[relay] +LINE_RECEIVER_INTERFACE = 0.0.0.0 +LINE_RECEIVER_PORT = 2013 +PICKLE_RECEIVER_INTERFACE = 0.0.0.0 +PICKLE_RECEIVER_PORT = 2014 + +# To use consistent hashing instead of the user defined relay-rules.conf, +# change this to: +# RELAY_METHOD = consistent-hashing +RELAY_METHOD = rules + +# If you use consistent-hashing you may want to add redundancy +# of your data by replicating every datapoint to more than +# one machine. +REPLICATION_FACTOR = 1 + +# This is a list of carbon daemons we will send any relayed or +# generated metrics to. The default provided would send to a single +# carbon-cache instance on the default port. However if you +# use multiple carbon-cache instances then it would look like this: +# +# DESTINATIONS = 127.0.0.1:2004:a, 127.0.0.1:2104:b +# +# The general form is IP:PORT:INSTANCE where the :INSTANCE part is +# optional and refers to the "None" instance if omitted. +# +# Note that if the destinations are all carbon-caches then this should +# exactly match the webapp's CARBONLINK_HOSTS setting in terms of +# instances listed (order matters!). +# +# If using RELAY_METHOD = rules, all destinations used in relay-rules.conf +# must be defined in this list +DESTINATIONS = 127.0.0.1:2004 + +# This defines the maximum "message size" between carbon daemons. +# You shouldn't need to tune this unless you really know what you're doing. +MAX_DATAPOINTS_PER_MESSAGE = 500 +MAX_QUEUE_SIZE = 10000 + +# Set this to False to drop datapoints when any send queue (sending datapoints +# to a downstream carbon daemon) hits MAX_QUEUE_SIZE. If this is True (the +# default) then sockets over which metrics are received will temporarily stop accepting +# data until the send queues fall below 80% MAX_QUEUE_SIZE. +USE_FLOW_CONTROL = True + +# Set this to True to enable whitelisting and blacklisting of metrics in +# CONF_DIR/whitelist and CONF_DIR/blacklist. If the whitelist is missing or +# empty, all metrics will pass through +# USE_WHITELIST = False + +# By default, carbon itself will log statistics (such as a count, +# metricsReceived) with the top level prefix of 'carbon' at an interval of 60 +# seconds. Set CARBON_METRIC_INTERVAL to 0 to disable instrumentation +# CARBON_METRIC_PREFIX = carbon +# CARBON_METRIC_INTERVAL = 60 + + +[aggregator] +LINE_RECEIVER_INTERFACE = 0.0.0.0 +LINE_RECEIVER_PORT = 2023 + +PICKLE_RECEIVER_INTERFACE = 0.0.0.0 +PICKLE_RECEIVER_PORT = 2024 + +# This is a list of carbon daemons we will send any relayed or +# generated metrics to. The default provided would send to a single +# carbon-cache instance on the default port. However if you +# use multiple carbon-cache instances then it would look like this: +# +# DESTINATIONS = 127.0.0.1:2004:a, 127.0.0.1:2104:b +# +# The format is comma-delimited IP:PORT:INSTANCE where the :INSTANCE part is +# optional and refers to the "None" instance if omitted. +# +# Note that if the destinations are all carbon-caches then this should +# exactly match the webapp's CARBONLINK_HOSTS setting in terms of +# instances listed (order matters!). +DESTINATIONS = 127.0.0.1:2004 + +# If you want to add redundancy to your data by replicating every +# datapoint to more than one machine, increase this. +REPLICATION_FACTOR = 1 + +# This is the maximum number of datapoints that can be queued up +# for a single destination. Once this limit is hit, we will +# stop accepting new data if USE_FLOW_CONTROL is True, otherwise +# we will drop any subsequently received datapoints. +MAX_QUEUE_SIZE = 10000 + +# Set this to False to drop datapoints when any send queue (sending datapoints +# to a downstream carbon daemon) hits MAX_QUEUE_SIZE. If this is True (the +# default) then sockets over which metrics are received will temporarily stop accepting +# data until the send queues fall below 80% MAX_QUEUE_SIZE. +USE_FLOW_CONTROL = True + +# This defines the maximum "message size" between carbon daemons. +# You shouldn't need to tune this unless you really know what you're doing. +MAX_DATAPOINTS_PER_MESSAGE = 500 + +# This defines how many datapoints the aggregator remembers for +# each metric. Aggregation only happens for datapoints that fall in +# the past MAX_AGGREGATION_INTERVALS * intervalSize seconds. +MAX_AGGREGATION_INTERVALS = 5 + +# Set this to True to enable whitelisting and blacklisting of metrics in +# CONF_DIR/whitelist and CONF_DIR/blacklist. If the whitelist is missing or +# empty, all metrics will pass through +# USE_WHITELIST = False + +# By default, carbon itself will log statistics (such as a count, +# metricsReceived) with the top level prefix of 'carbon' at an interval of 60 +# seconds. Set CARBON_METRIC_INTERVAL to 0 to disable instrumentation +# CARBON_METRIC_PREFIX = carbon +# CARBON_METRIC_INTERVAL = 60 diff --git a/modules/graphite/templates/config.js.erb b/modules/graphite/templates/config.js.erb new file mode 100644 index 0000000000..cb23f5119d --- /dev/null +++ b/modules/graphite/templates/config.js.erb @@ -0,0 +1,57 @@ +/* + +Required Variables: + + port: StatsD listening port [default: 8125] + +Graphite Required Variables: + +(Leave these unset to avoid sending stats to Graphite. + Set debug flag and leave these unset to run in 'dry' debug mode - + useful for testing statsd clients without a Graphite server.) + + graphiteHost: hostname or IP of Graphite server + graphitePort: port of Graphite server + +Optional Variables: + + backends: an array of backends to load. Each backend must exist + by name in the directory backends/. If not specified, + the default graphite backend will be loaded. + debug: debug flag [default: false] + address: address to listen on over UDP [default: 0.0.0.0] + port: port to listen for messages on over UDP [default: 8125] + mgmt_address: address to run the management TCP interface on + [default: 0.0.0.0] + mgmt_port: port to run the management TCP interface on [default: 8126] + debugInterval: interval to print debug information [ms, default: 10000] + dumpMessages: log all incoming messages + flushInterval: interval (in ms) to flush to Graphite + percentThreshold: for time information, calculate the Nth percentile(s) + (can be a single value or list of floating-point values) + [%, default: 90] + keyFlush: log the most frequently sent keys [object, default: undefined] + interval: how often to log frequent keys [ms, default: 0] + percent: percentage of frequent keys to log [%, default: 100] + log: location of log file for frequent keys [default: STDOUT] + + console: + prettyprint: whether to prettyprint the console backend + output [true or false, default: true] + + log: log settings [object, default: undefined] + backend: where to log: stdout or syslog [string, default: stdout] + application: name of the application for syslog [string, default: statsd] + level: log level for [node-]syslog [string, default: LOG_INFO] + + repeater: an array of hashes of the for host: and port: + that details other statsd servers to which the received + packets should be "repeated" (duplicated to). + e.g. [ { host: '10.10.10.10', port: 8125 }, + { host: 'observer', port: 88125 } ] +*/ +{ + graphitePort: 2003 +, graphiteHost: "127.0.0.1" +, port: 8125 +} diff --git a/modules/graphite/templates/graphite.vhost.erb b/modules/graphite/templates/graphite.vhost.erb new file mode 100644 index 0000000000..ef233819a6 --- /dev/null +++ b/modules/graphite/templates/graphite.vhost.erb @@ -0,0 +1,56 @@ +# This needs to be in your server's config somewhere, probably +# the main httpd.conf +# NameVirtualHost *:80 + +# This line also needs to be in your server's config. +# LoadModule wsgi_module modules/mod_wsgi.so + +# You need to manually edit this file to fit your needs. +# This configuration assumes the default installation prefix +# of /opt/graphite/, if you installed graphite somewhere else +# you will need to change all the occurances of /opt/graphite/ +# in this file to your chosen install location. + +# XXX You need to set this up! +# Read http://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGISocketPrefix +#WSGISocketPrefix /var/run/apache2/wsgi + + + DocumentRoot "/var/lib/graphite/webapp" + ErrorLog /var/log/apache2/graphite-error.log + CustomLog /var/log/apache2/graphite-access.log common + + # I've found that an equal number of processes & threads tends + # to show the best performance for Graphite (ymmv). + WSGIDaemonProcess graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 + WSGIProcessGroup graphite + WSGIApplicationGroup %{GLOBAL} + SetEnv GRAPHITE_STORAGE_DIR /var/lib/graphite + WSGIImportScript /etc/graphite/graphite.wsgi process-group=graphite application-group=%{GLOBAL} + + # XXX You will need to create this file! There is a graphite.wsgi.example + # file in this directory that you can safely use, just copy it to graphite.wgsi + WSGIScriptAlias / /etc/graphite/graphite.wsgi + + Alias /content/ /var/lib/graphite/webapp/content/ + + SetHandler None + + + # XXX In order for the django admin site media to work you + # must change @DJANGO_ROOT@ to be the path to your django + # installation, which is probably something like: + # /usr/lib/python2.6/site-packages/django + Alias /media/ "/usr/lib/python2.7/dist-packages/django/contrib/admin/media/" + + SetHandler None + + + # The graphite.wsgi file has to be accessible by apache. It won't + # be visible to clients because of the DocumentRoot though. + + Order deny,allow + Allow from all + + + diff --git a/modules/graphite/templates/graphite.wsgi.erb b/modules/graphite/templates/graphite.wsgi.erb new file mode 100644 index 0000000000..901de68d28 --- /dev/null +++ b/modules/graphite/templates/graphite.wsgi.erb @@ -0,0 +1,16 @@ +import os, sys +sys.path.append('/var/lib/graphite/webapp') +os.environ['DJANGO_SETTINGS_MODULE'] = 'graphite.settings' + +import django.core.handlers.wsgi + +application = django.core.handlers.wsgi.WSGIHandler() + +# READ THIS +# Initializing the search index can be very expensive, please include +# the WSGIScriptImport directive pointing to this script in your vhost +# config to ensure the index is preloaded before any requests are handed +# to the process. +from graphite.logger import log +log.info("graphite.wsgi - pid %d - reloading search index" % os.getpid()) +import graphite.metrics.search diff --git a/modules/graphite/templates/local_settings.py.erb b/modules/graphite/templates/local_settings.py.erb new file mode 100644 index 0000000000..5696a33f92 --- /dev/null +++ b/modules/graphite/templates/local_settings.py.erb @@ -0,0 +1,3 @@ +STORAGE_DIR = '/var/lib/graphite/' +CONF_DIR = '/etc/graphite/' +LOG_DIR = '/var/log/graphite/' diff --git a/modules/graphite/templates/storage-schemas.conf.erb b/modules/graphite/templates/storage-schemas.conf.erb new file mode 100644 index 0000000000..d184c620d0 --- /dev/null +++ b/modules/graphite/templates/storage-schemas.conf.erb @@ -0,0 +1,16 @@ +# Schema definitions for Whisper files. Entries are scanned in order, +# and first match wins. This file is scanned for changes every 60 seconds. +# +# [name] +# pattern = regex +# retentions = timePerPoint:timeToStore, timePerPoint:timeToStore, ... + +# Carbon's internal metrics. This entry should match what is specified in +# CARBON_METRIC_PREFIX and CARBON_METRIC_INTERVAL settings +[carbon] +pattern = ^carbon\. +retentions = 60:90d + +[default_1min_for_3years] +pattern = .* +retentions = 60s:3y diff --git a/modules/openstack_project/manifests/graphite.pp b/modules/openstack_project/manifests/graphite.pp new file mode 100644 index 0000000000..5615393d37 --- /dev/null +++ b/modules/openstack_project/manifests/graphite.pp @@ -0,0 +1,26 @@ +# Class to configure graphite on a node. +# Takes a list of sysadmin email addresses as a parameter. Exim will be +# configured to email cron spam and other alerts to this list of admins. +class openstack_project::graphite ( + $sysadmins = [], + $graphite_admin_user = '', + $graphite_admin_email = '', + $graphite_admin_password ='', + $statsd_hosts = [], +) { + + # Turn a list of hostnames into a list of iptables rules + $rules = regsubst ($statsd_hosts, '^(.*)$', '-m udp -p udp -s \1 --dport 8125 -j ACCEPT') + + class { 'openstack_project::server': + iptables_public_tcp_ports => [80, 443], + iptables_rules4 => $rules, + sysadmins => $sysadmins, + } + + class { '::graphite': + graphite_admin_user => $graphite_admin_user, + graphite_admin_email => $graphite_admin_email, + graphite_admin_password => $graphite_admin_password, + } +}