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
1=== modified file 'README.md'
2--- README.md 2016-02-01 15:13:24 +0000
3+++ README.md 2016-03-30 20:53:04 +0000
4@@ -24,6 +24,29 @@
5
6 The only output plugin supported via relation is influxdb, any other output plugin needs to be configured manually (via juju set)
7
8+To 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:
9+
10+ inputs:
11+ cpu:
12+ percpu: 'false'
13+ fielddrop: '["time_*"]'
14+ disk:
15+ mount_points: '["/"]'
16+ ignore_fs: '["tmpfs", "devtmpfs"]'
17+ elasticsearch:
18+ local: 'false'
19+ cluster_health: 'true'
20+ postgresql:
21+ databases: '["foo", "bar"]'
22+ tagpass:
23+ db: '["template", "postgres"]'
24+ outputs:
25+ influxdb:
26+ precision: "ms"
27+
28+This extra options will only be applied to plugins defined in templates/base_inputs.conf and any other plugins configured via relations.
29+
30+
31 To use a different metrics storage, e.g: graphite. the plugin configuration needs to be set as a base64 string in outputs_config configuration.
32
33 For exmaple, save the following config to a file:
34
35=== modified file 'config.yaml'
36--- config.yaml 2016-02-25 18:36:59 +0000
37+++ config.yaml 2016-03-30 20:53:04 +0000
38@@ -66,7 +66,7 @@
39 default: ""
40 description: "[outputs.xxx] sections as base64 string"
41 package_name:
42- default: "telegraf_0.10.1-1_amd64.deb"
43+ default: "telegraf_0.10.1-1_amd64.deb"
44 type: string
45 description: |
46 Filename of telegraf deb package. If this matches the
47@@ -81,4 +81,28 @@
48 default: ""
49 type: string
50 description: "GPG key for apt_repository"
51+ extra_options:
52+ default: ""
53+ type: string
54+ description: |
55+ YAML with extra options for out|inputs managed by relations or in the default config.
56+ Values must be strings.
57+ example:
58+ inputs:
59+ cpu:
60+ percpu: 'false'
61+ fielddrop: '["time_*"]'
62+ disk:
63+ mount_points: '["/"]'
64+ ignore_fs: '["tmpfs", "devtmpfs"]'
65+ elasticsearch:
66+ local: 'false'
67+ cluster_health: 'true'
68+ postgresql:
69+ databases: '["foo", "bar"]'
70+ tagpass:
71+ db: '["template", "postgres"]'
72+ outputs:
73+ influxdb:
74+ precision: "ms"
75
76
77=== modified file 'hooks/actions.py'
78--- hooks/actions.py 2016-03-11 15:36:40 +0000
79+++ hooks/actions.py 2016-03-30 20:53:04 +0000
80@@ -4,11 +4,15 @@
81 import subprocess
82 import sys
83
84+import yaml
85+
86 from charmhelpers.core import hookenv, host
87 from charmhelpers.core.templating import render
88 from charmhelpers.core.services.base import ManagerCallback
89 from charmhelpers.fetch import apt_install, apt_update, add_source
90
91+from jinja2 import Template
92+
93
94 CONFIG_FILE = '/etc/telegraf/telegraf.conf'
95
96@@ -32,6 +36,8 @@
97 return old
98
99 def __call__(self, manager, service_name, event_name):
100+ if hookenv.hook_name() in ('update-status',):
101+ return
102 new_hash = host.file_hash(CONFIG_FILE)
103 old_hash = self._update_persisted_data('config_hash', new_hash)
104 if new_hash != old_hash:
105@@ -59,7 +65,9 @@
106 pkg_file = os.path.join(hookenv.charm_dir(), "files",
107 config['package_name'])
108 if os.path.exists(pkg_file):
109- if subprocess.call(["dpkg", "-i", pkg_file]) != 0:
110+ env = os.environ.copy()
111+ env['DEBIAN_FRONTEND'] = 'noninteractive'
112+ if subprocess.call(["dpkg", "-i", "--force-confold", pkg_file], env=env) != 0:
113 hookenv.log("CHARM: Error installing telegraf package")
114 sys.exit(1)
115 else:
116@@ -70,12 +78,22 @@
117 apt_install(config['package_name'], options=['--force-yes'])
118
119
120+def update_telegraf(service_name):
121+ config = hookenv.config()
122+ if hookenv.hook_name() == 'config-changed' \
123+ and config.changed('package_name'):
124+ install()
125+ elif hookenv.hook_name() == 'upgrade-charm':
126+ install()
127+
128+
129 def log_start(service_name):
130 hookenv.log('telegraf starting')
131
132
133 def render_config(service_name):
134 config = hookenv.config()
135+ extra_options = get_extra_options()
136 context = config.copy()
137 inputs = base64.b64decode(config['inputs_config'])
138 outputs = base64.b64decode(config['outputs_config'])
139@@ -90,7 +108,9 @@
140 else:
141 # use base inputs from charm templates
142 with open('templates/base_inputs.conf', 'r') as fd:
143- context["inputs"] = fd.read()
144+ context["inputs"] = render_template(
145+ fd.read(),
146+ {'extra_options': extra_options['inputs']})
147 if outputs:
148 context["outputs"] = outputs
149 else:
150@@ -119,12 +139,45 @@
151 return rels[0]['__unit__']
152
153
154+def get_extra_options():
155+ extra_options = {'inputs': {}, 'outpus': {}}
156+ extra_options_raw = hookenv.config()['extra_options']
157+ extra_opts = yaml.load(extra_options_raw) or {}
158+ extra_options.update(extra_opts)
159+ return extra_options
160+
161+
162+def render_extra_options(kind, name):
163+ template = """
164+ {% if extra_options %}
165+ {% for key, value in extra_options.items() %}
166+ {% if key == 'tagpass' or key == 'tagdrop' %}
167+ [{{ kind }}.{{ name }}.{{key}}]
168+ {% for tag, tagvalue in value.items() %}
169+ {{ tag }} = {{ tagvalue }}
170+ {% endfor %}
171+ {% else %}
172+ {{ key }} = {{ value }}
173+ {% endif %}
174+ {% endfor %}
175+ {% endif %}
176+ """
177+ extra_options = get_extra_options()
178+ context = {"extra_options": extra_options[kind].get(name, {}),
179+ "kind": kind,
180+ "name": name}
181+ return render_template(template, context)
182+
183+
184+def render_template(template, context):
185+ tmpl = Template(template, lstrip_blocks=True, trim_blocks=True)
186+ return tmpl.render(**context)
187+
188+
189 def elasticsearch_input(service_name):
190 template = """
191-[elasticsearch]
192- servers = {}
193- local = true
194- cluster_health = true
195+[[inputs.elasticsearch]]
196+ servers = {{ servers }}
197 """
198 rels = hookenv.relations_of_type('elasticsearch')
199 hosts = []
200@@ -138,7 +191,9 @@
201 hosts.append("http://{}:{}".format(es_host, port))
202 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'elasticsearch')
203 if hosts:
204- inputs.append(template.format(json.dumps(hosts)))
205+ context = {"servers": json.dumps(hosts)}
206+ inputs.append(render_template(template, context) + \
207+ render_extra_options("inputs", "elasticsearch"))
208 hookenv.log("Updating {} plugin config file".format('elasticsearch'))
209 host.write_file(config_path, '\n'.join(inputs))
210 elif os.path.exists(config_path):
211@@ -148,7 +203,7 @@
212 def mongodb_input(service_name):
213 template = """
214 [[inputs.mongodb]]
215- servers = {}
216+ servers = {{ servers }}
217 """
218 rels = hookenv.relations_of_type('mongodb')
219 mongo_addresses = []
220@@ -163,7 +218,10 @@
221 mongo_addresses.append(mongo_address)
222 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'mongodb')
223 if mongo_addresses:
224- inputs.append(template.format(json.dumps(mongo_addresses)))
225+ extra_options = get_extra_options()
226+ context = {"servers": json.dumps(mongo_addresses)}
227+ inputs.append(render_template(template, context) + \
228+ render_extra_options("inputs", "mongodb"))
229 hookenv.log("Updating {} plugin config file".format('mongodb'))
230 host.write_file(config_path, '\n'.join(inputs))
231 elif os.path.exists(config_path):
232@@ -173,15 +231,18 @@
233 def postgresql_input(service_name):
234 template = """
235 [[inputs.postgresql]]
236- address = "host={host} user={user} password={password} dbname={database}"
237+ address = "host={{host}} user={{user}} password={{password}} dbname={{database}}"
238 """
239 required_keys = ['host', 'user', 'password', 'database']
240 rels = hookenv.relations_of_type('postgresql')
241+ extra_options = get_extra_options()
242 inputs = []
243 for rel in rels:
244 if all([rel.get(key) for key in required_keys]) \
245 and hookenv.local_unit() in rel.get('allowed-units'):
246- inputs.append(template.format(**rel))
247+ context = rel.copy()
248+ inputs.append(render_template(template, context) + \
249+ render_extra_options("inputs", "postgresql"))
250 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'postgresql')
251 if inputs:
252 hookenv.log("Updating {} plugin config file".format('postgresql'))
253@@ -193,7 +254,7 @@
254 def haproxy_input(service_name):
255 template = """
256 [[inputs.haproxy]]
257- servers = {}
258+ servers = {{ servers }}
259 """
260 rels = hookenv.relations_of_type('haproxy')
261 haproxy_addresses = []
262@@ -220,7 +281,8 @@
263 haproxy_addresses.append(haproxy_address)
264 config_path = '{}/{}.conf'.format(CONFIG_DIR, 'haproxy')
265 if haproxy_addresses:
266- inputs.append(template.format(json.dumps(haproxy_addresses)))
267+ inputs.append(render_template(template, {"servers": json.dumps(haproxy_addresses)}) + \
268+ render_extra_options("inputs", "haproxy"))
269 hookenv.log("Updating {} plugin config file".format('haproxy'))
270 host.write_file(config_path, '\n'.join(inputs))
271 elif os.path.exists(config_path):
272
273=== modified file 'hooks/services.py'
274--- hooks/services.py 2016-03-03 19:44:28 +0000
275+++ hooks/services.py 2016-03-30 20:53:04 +0000
276@@ -15,6 +15,7 @@
277 'required_data': [
278 ],
279 'data_ready': [
280+ actions.update_telegraf,
281 actions.render_config,
282 actions.elasticsearch_input,
283 actions.mongodb_input,
284
285=== modified file 'templates/base_inputs.conf'
286--- templates/base_inputs.conf 2016-02-25 21:08:33 +0000
287+++ templates/base_inputs.conf 2016-03-30 20:53:04 +0000
288@@ -1,17 +1,37 @@
289+{% macro render_options(name, options) %}
290+ {% if options[name] %}
291+ {% for key, value in options[name].items() %}
292+ {% if key == 'tagpass' or key == 'tagdrop' %}
293+ [inputs.{{ name }}.{{key}}]
294+ {% for tag, tagvalue in value.items() %}
295+ {{ tag }} = {{ tagvalue }}
296+ {% endfor %}
297+ {% else %}
298+ {{ key }} = {{ value }}
299+ {% endif %}
300+ {% endfor %}
301+ {% endif %}
302+{% endmacro %}
303+
304 # Read metrics about cpu usage
305 [[inputs.cpu]]
306+ {% if extra_options['cpu'] %}
307+{{ render_options('cpu', extra_options) }}
308+ {% else %}
309 # Whether to report per-cpu stats or not
310 percpu = true
311 # Whether to report total system cpu stats or not
312 totalcpu = true
313 # Comment this line if you want the raw CPU time metrics
314 drop = ["time_*"]
315+ {% endif %}
316
317 # Read metrics about disk usage by mount point
318 [[inputs.disk]]
319 # By default, telegraf gather stats for all mountpoints.
320 # Setting mountpoints will restrict the stats to the specified mountpoints.
321 # mount_points=["/"]
322+{{ render_options('disk', extra_options) }}
323
324 # Read metrics about disk IO by device
325 [[inputs.diskio]]
326@@ -21,6 +41,7 @@
327 # devices = ["sda", "sdb"]
328 # Uncomment the following line if you do not need disk serial numbers.
329 # skip_serial_number = true
330+{{ render_options('diskio', extra_options) }}
331
332 # Read metrics about memory usage
333 [[inputs.mem]]
334@@ -33,6 +54,7 @@
335 # regardless of status.
336 #
337 # interfaces = ["eth0", ... ]
338+{{ render_options('net', extra_options) }}
339
340 # Read metrics about TCP status such as established, time wait etc and UDP sockets counts.
341 [[inputs.netstat]]

Subscribers

People subscribed via source and target branches