Merge lp:~verterok/charms/trusty/telegraf/proper-templates into lp:~ubuntuone-hackers/charms/trusty/telegraf/trunk

Proposed by Guillermo Gonzalez
Status: Merged
Approved by: Guillermo Gonzalez
Approved revision: 17
Merged at revision: 16
Proposed branch: lp:~verterok/charms/trusty/telegraf/proper-templates
Merge into: lp:~ubuntuone-hackers/charms/trusty/telegraf/trunk
Diff against target: 341 lines (+146/-14)
5 files modified
README.md (+23/-0)
config.yaml (+25/-1)
hooks/actions.py (+75/-13)
hooks/services.py (+1/-0)
templates/base_inputs.conf (+22/-0)
To merge this branch: bzr merge lp:~verterok/charms/trusty/telegraf/proper-templates
Reviewer Review Type Date Requested Status
Bret Barker (community) Approve
Review via email: mp+290512@code.launchpad.net

Commit message

Support telegraf updates, use jinja templates for plugin templates and add extra_options config to be able to fine tune each plugin

Description of the change

- support telegraf updates
- use jinja templates for plugin templates
- add extra_options config, to be able to fine tune each plugin

To post a comment you must log in.
Revision history for this message
Bret Barker (noise) wrote :

wording nitpick below, otherwise good job!

review: Approve
17. By Guillermo Gonzalez

fix README wording

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README.md'
--- README.md 2016-02-01 15:13:24 +0000
+++ README.md 2016-03-30 20:53:04 +0000
@@ -24,6 +24,29 @@
2424
25The only output plugin supported via relation is influxdb, any other output plugin needs to be configured manually (via juju set)25The only output plugin supported via relation is influxdb, any other output plugin needs to be configured manually (via juju set)
2626
27To configure any of the (default or via relation) plugins, the extra_options charm config can be used. It's string in yaml format, for example:
28
29 inputs:
30 cpu:
31 percpu: 'false'
32 fielddrop: '["time_*"]'
33 disk:
34 mount_points: '["/"]'
35 ignore_fs: '["tmpfs", "devtmpfs"]'
36 elasticsearch:
37 local: 'false'
38 cluster_health: 'true'
39 postgresql:
40 databases: '["foo", "bar"]'
41 tagpass:
42 db: '["template", "postgres"]'
43 outputs:
44 influxdb:
45 precision: "ms"
46
47This extra options will only be applied to plugins defined in templates/base_inputs.conf and any other plugins configured via relations.
48
49
27To use a different metrics storage, e.g: graphite. the plugin configuration needs to be set as a base64 string in outputs_config configuration.50To use a different metrics storage, e.g: graphite. the plugin configuration needs to be set as a base64 string in outputs_config configuration.
2851
29For exmaple, save the following config to a file: 52For exmaple, save the following config to a file:
3053
=== modified file 'config.yaml'
--- config.yaml 2016-02-25 18:36:59 +0000
+++ config.yaml 2016-03-30 20:53:04 +0000
@@ -66,7 +66,7 @@
66 default: ""66 default: ""
67 description: "[outputs.xxx] sections as base64 string"67 description: "[outputs.xxx] sections as base64 string"
68 package_name:68 package_name:
69 default: "telegraf_0.10.1-1_amd64.deb"69 default: "telegraf_0.10.1-1_amd64.deb"
70 type: string70 type: string
71 description: |71 description: |
72 Filename of telegraf deb package. If this matches the72 Filename of telegraf deb package. If this matches the
@@ -81,4 +81,28 @@
81 default: ""81 default: ""
82 type: string82 type: string
83 description: "GPG key for apt_repository"83 description: "GPG key for apt_repository"
84 extra_options:
85 default: ""
86 type: string
87 description: |
88 YAML with extra options for out|inputs managed by relations or in the default config.
89 Values must be strings.
90 example:
91 inputs:
92 cpu:
93 percpu: 'false'
94 fielddrop: '["time_*"]'
95 disk:
96 mount_points: '["/"]'
97 ignore_fs: '["tmpfs", "devtmpfs"]'
98 elasticsearch:
99 local: 'false'
100 cluster_health: 'true'
101 postgresql:
102 databases: '["foo", "bar"]'
103 tagpass:
104 db: '["template", "postgres"]'
105 outputs:
106 influxdb:
107 precision: "ms"
84108
85109
=== modified file 'hooks/actions.py'
--- hooks/actions.py 2016-03-11 15:36:40 +0000
+++ hooks/actions.py 2016-03-30 20:53:04 +0000
@@ -4,11 +4,15 @@
4import subprocess4import subprocess
5import sys5import sys
66
7import yaml
8
7from charmhelpers.core import hookenv, host9from charmhelpers.core import hookenv, host
8from charmhelpers.core.templating import render10from charmhelpers.core.templating import render
9from charmhelpers.core.services.base import ManagerCallback11from charmhelpers.core.services.base import ManagerCallback
10from charmhelpers.fetch import apt_install, apt_update, add_source12from charmhelpers.fetch import apt_install, apt_update, add_source
1113
14from jinja2 import Template
15
1216
13CONFIG_FILE = '/etc/telegraf/telegraf.conf'17CONFIG_FILE = '/etc/telegraf/telegraf.conf'
1418
@@ -32,6 +36,8 @@
32 return old36 return old
3337
34 def __call__(self, manager, service_name, event_name):38 def __call__(self, manager, service_name, event_name):
39 if hookenv.hook_name() in ('update-status',):
40 return
35 new_hash = host.file_hash(CONFIG_FILE)41 new_hash = host.file_hash(CONFIG_FILE)
36 old_hash = self._update_persisted_data('config_hash', new_hash)42 old_hash = self._update_persisted_data('config_hash', new_hash)
37 if new_hash != old_hash:43 if new_hash != old_hash:
@@ -59,7 +65,9 @@
59 pkg_file = os.path.join(hookenv.charm_dir(), "files",65 pkg_file = os.path.join(hookenv.charm_dir(), "files",
60 config['package_name'])66 config['package_name'])
61 if os.path.exists(pkg_file):67 if os.path.exists(pkg_file):
62 if subprocess.call(["dpkg", "-i", pkg_file]) != 0:68 env = os.environ.copy()
69 env['DEBIAN_FRONTEND'] = 'noninteractive'
70 if subprocess.call(["dpkg", "-i", "--force-confold", pkg_file], env=env) != 0:
63 hookenv.log("CHARM: Error installing telegraf package")71 hookenv.log("CHARM: Error installing telegraf package")
64 sys.exit(1)72 sys.exit(1)
65 else:73 else:
@@ -70,12 +78,22 @@
70 apt_install(config['package_name'], options=['--force-yes'])78 apt_install(config['package_name'], options=['--force-yes'])
7179
7280
81def update_telegraf(service_name):
82 config = hookenv.config()
83 if hookenv.hook_name() == 'config-changed' \
84 and config.changed('package_name'):
85 install()
86 elif hookenv.hook_name() == 'upgrade-charm':
87 install()
88
89
73def log_start(service_name):90def log_start(service_name):
74 hookenv.log('telegraf starting')91 hookenv.log('telegraf starting')
7592
7693
77def render_config(service_name):94def render_config(service_name):
78 config = hookenv.config()95 config = hookenv.config()
96 extra_options = get_extra_options()
79 context = config.copy()97 context = config.copy()
80 inputs = base64.b64decode(config['inputs_config'])98 inputs = base64.b64decode(config['inputs_config'])
81 outputs = base64.b64decode(config['outputs_config'])99 outputs = base64.b64decode(config['outputs_config'])
@@ -90,7 +108,9 @@
90 else:108 else:
91 # use base inputs from charm templates109 # use base inputs from charm templates
92 with open('templates/base_inputs.conf', 'r') as fd:110 with open('templates/base_inputs.conf', 'r') as fd:
93 context["inputs"] = fd.read()111 context["inputs"] = render_template(
112 fd.read(),
113 {'extra_options': extra_options['inputs']})
94 if outputs:114 if outputs:
95 context["outputs"] = outputs115 context["outputs"] = outputs
96 else:116 else:
@@ -119,12 +139,45 @@
119 return rels[0]['__unit__']139 return rels[0]['__unit__']
120140
121141
142def get_extra_options():
143 extra_options = {'inputs': {}, 'outpus': {}}
144 extra_options_raw = hookenv.config()['extra_options']
145 extra_opts = yaml.load(extra_options_raw) or {}
146 extra_options.update(extra_opts)
147 return extra_options
148
149
150def render_extra_options(kind, name):
151 template = """
152 {% if extra_options %}
153 {% for key, value in extra_options.items() %}
154 {% if key == 'tagpass' or key == 'tagdrop' %}
155 [{{ kind }}.{{ name }}.{{key}}]
156 {% for tag, tagvalue in value.items() %}
157 {{ tag }} = {{ tagvalue }}
158 {% endfor %}
159 {% else %}
160 {{ key }} = {{ value }}
161 {% endif %}
162 {% endfor %}
163 {% endif %}
164 """
165 extra_options = get_extra_options()
166 context = {"extra_options": extra_options[kind].get(name, {}),
167 "kind": kind,
168 "name": name}
169 return render_template(template, context)
170
171
172def render_template(template, context):
173 tmpl = Template(template, lstrip_blocks=True, trim_blocks=True)
174 return tmpl.render(**context)
175
176
122def elasticsearch_input(service_name):177def elasticsearch_input(service_name):
123 template = """178 template = """
124[elasticsearch]179[[inputs.elasticsearch]]
125 servers = {}180 servers = {{ servers }}
126 local = true
127 cluster_health = true
128"""181"""
129 rels = hookenv.relations_of_type('elasticsearch')182 rels = hookenv.relations_of_type('elasticsearch')
130 hosts = []183 hosts = []
@@ -138,7 +191,9 @@
138 hosts.append("http://{}:{}".format(es_host, port))191 hosts.append("http://{}:{}".format(es_host, port))
139 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'elasticsearch')192 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'elasticsearch')
140 if hosts:193 if hosts:
141 inputs.append(template.format(json.dumps(hosts)))194 context = {"servers": json.dumps(hosts)}
195 inputs.append(render_template(template, context) + \
196 render_extra_options("inputs", "elasticsearch"))
142 hookenv.log("Updating {} plugin config file".format('elasticsearch'))197 hookenv.log("Updating {} plugin config file".format('elasticsearch'))
143 host.write_file(config_path, '\n'.join(inputs))198 host.write_file(config_path, '\n'.join(inputs))
144 elif os.path.exists(config_path):199 elif os.path.exists(config_path):
@@ -148,7 +203,7 @@
148def mongodb_input(service_name):203def mongodb_input(service_name):
149 template = """204 template = """
150[[inputs.mongodb]]205[[inputs.mongodb]]
151 servers = {}206 servers = {{ servers }}
152"""207"""
153 rels = hookenv.relations_of_type('mongodb')208 rels = hookenv.relations_of_type('mongodb')
154 mongo_addresses = []209 mongo_addresses = []
@@ -163,7 +218,10 @@
163 mongo_addresses.append(mongo_address)218 mongo_addresses.append(mongo_address)
164 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'mongodb')219 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'mongodb')
165 if mongo_addresses:220 if mongo_addresses:
166 inputs.append(template.format(json.dumps(mongo_addresses)))221 extra_options = get_extra_options()
222 context = {"servers": json.dumps(mongo_addresses)}
223 inputs.append(render_template(template, context) + \
224 render_extra_options("inputs", "mongodb"))
167 hookenv.log("Updating {} plugin config file".format('mongodb'))225 hookenv.log("Updating {} plugin config file".format('mongodb'))
168 host.write_file(config_path, '\n'.join(inputs))226 host.write_file(config_path, '\n'.join(inputs))
169 elif os.path.exists(config_path):227 elif os.path.exists(config_path):
@@ -173,15 +231,18 @@
173def postgresql_input(service_name):231def postgresql_input(service_name):
174 template = """232 template = """
175[[inputs.postgresql]]233[[inputs.postgresql]]
176 address = "host={host} user={user} password={password} dbname={database}"234 address = "host={{host}} user={{user}} password={{password}} dbname={{database}}"
177"""235"""
178 required_keys = ['host', 'user', 'password', 'database']236 required_keys = ['host', 'user', 'password', 'database']
179 rels = hookenv.relations_of_type('postgresql')237 rels = hookenv.relations_of_type('postgresql')
238 extra_options = get_extra_options()
180 inputs = []239 inputs = []
181 for rel in rels:240 for rel in rels:
182 if all([rel.get(key) for key in required_keys]) \241 if all([rel.get(key) for key in required_keys]) \
183 and hookenv.local_unit() in rel.get('allowed-units'):242 and hookenv.local_unit() in rel.get('allowed-units'):
184 inputs.append(template.format(**rel))243 context = rel.copy()
244 inputs.append(render_template(template, context) + \
245 render_extra_options("inputs", "postgresql"))
185 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'postgresql')246 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'postgresql')
186 if inputs:247 if inputs:
187 hookenv.log("Updating {} plugin config file".format('postgresql'))248 hookenv.log("Updating {} plugin config file".format('postgresql'))
@@ -193,7 +254,7 @@
193def haproxy_input(service_name):254def haproxy_input(service_name):
194 template = """255 template = """
195[[inputs.haproxy]]256[[inputs.haproxy]]
196 servers = {}257 servers = {{ servers }}
197"""258"""
198 rels = hookenv.relations_of_type('haproxy')259 rels = hookenv.relations_of_type('haproxy')
199 haproxy_addresses = []260 haproxy_addresses = []
@@ -220,7 +281,8 @@
220 haproxy_addresses.append(haproxy_address)281 haproxy_addresses.append(haproxy_address)
221 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'haproxy')282 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'haproxy')
222 if haproxy_addresses:283 if haproxy_addresses:
223 inputs.append(template.format(json.dumps(haproxy_addresses)))284 inputs.append(render_template(template, {"servers": json.dumps(haproxy_addresses)}) + \
285 render_extra_options("inputs", "haproxy"))
224 hookenv.log("Updating {} plugin config file".format('haproxy'))286 hookenv.log("Updating {} plugin config file".format('haproxy'))
225 host.write_file(config_path, '\n'.join(inputs))287 host.write_file(config_path, '\n'.join(inputs))
226 elif os.path.exists(config_path):288 elif os.path.exists(config_path):
227289
=== modified file 'hooks/services.py'
--- hooks/services.py 2016-03-03 19:44:28 +0000
+++ hooks/services.py 2016-03-30 20:53:04 +0000
@@ -15,6 +15,7 @@
15 'required_data': [15 'required_data': [
16 ],16 ],
17 'data_ready': [17 'data_ready': [
18 actions.update_telegraf,
18 actions.render_config,19 actions.render_config,
19 actions.elasticsearch_input,20 actions.elasticsearch_input,
20 actions.mongodb_input,21 actions.mongodb_input,
2122
=== modified file 'templates/base_inputs.conf'
--- templates/base_inputs.conf 2016-02-25 21:08:33 +0000
+++ templates/base_inputs.conf 2016-03-30 20:53:04 +0000
@@ -1,17 +1,37 @@
1{% macro render_options(name, options) %}
2 {% if options[name] %}
3 {% for key, value in options[name].items() %}
4 {% if key == 'tagpass' or key == 'tagdrop' %}
5 [inputs.{{ name }}.{{key}}]
6 {% for tag, tagvalue in value.items() %}
7 {{ tag }} = {{ tagvalue }}
8 {% endfor %}
9 {% else %}
10 {{ key }} = {{ value }}
11 {% endif %}
12 {% endfor %}
13 {% endif %}
14{% endmacro %}
15
1# Read metrics about cpu usage16# Read metrics about cpu usage
2[[inputs.cpu]]17[[inputs.cpu]]
18 {% if extra_options['cpu'] %}
19{{ render_options('cpu', extra_options) }}
20 {% else %}
3 # Whether to report per-cpu stats or not21 # Whether to report per-cpu stats or not
4 percpu = true22 percpu = true
5 # Whether to report total system cpu stats or not23 # Whether to report total system cpu stats or not
6 totalcpu = true24 totalcpu = true
7 # Comment this line if you want the raw CPU time metrics25 # Comment this line if you want the raw CPU time metrics
8 drop = ["time_*"]26 drop = ["time_*"]
27 {% endif %}
928
10# Read metrics about disk usage by mount point29# Read metrics about disk usage by mount point
11[[inputs.disk]]30[[inputs.disk]]
12 # By default, telegraf gather stats for all mountpoints.31 # By default, telegraf gather stats for all mountpoints.
13 # Setting mountpoints will restrict the stats to the specified mountpoints.32 # Setting mountpoints will restrict the stats to the specified mountpoints.
14 # mount_points=["/"]33 # mount_points=["/"]
34{{ render_options('disk', extra_options) }}
1535
16# Read metrics about disk IO by device36# Read metrics about disk IO by device
17[[inputs.diskio]]37[[inputs.diskio]]
@@ -21,6 +41,7 @@
21 # devices = ["sda", "sdb"]41 # devices = ["sda", "sdb"]
22 # Uncomment the following line if you do not need disk serial numbers.42 # Uncomment the following line if you do not need disk serial numbers.
23 # skip_serial_number = true43 # skip_serial_number = true
44{{ render_options('diskio', extra_options) }}
2445
25# Read metrics about memory usage46# Read metrics about memory usage
26[[inputs.mem]]47[[inputs.mem]]
@@ -33,6 +54,7 @@
33 # regardless of status.54 # regardless of status.
34 #55 #
35 # interfaces = ["eth0", ... ]56 # interfaces = ["eth0", ... ]
57{{ render_options('net', extra_options) }}
3658
37# Read metrics about TCP status such as established, time wait etc and UDP sockets counts.59# Read metrics about TCP status such as established, time wait etc and UDP sockets counts.
38[[inputs.netstat]]60[[inputs.netstat]]

Subscribers

People subscribed via source and target branches