Merge ~aieri/charm-prometheus-blackbox-exporter:bb-sub into ~prometheus-charmers/charm-prometheus-blackbox-exporter:master

Proposed by Andrea Ieri
Status: Rejected
Rejected by: Haw Loeung
Proposed branch: ~aieri/charm-prometheus-blackbox-exporter:bb-sub
Merge into: ~prometheus-charmers/charm-prometheus-blackbox-exporter:master
Diff against target: 282 lines (+192/-15)
5 files modified
config.yaml (+7/-0)
layer.yaml (+7/-2)
metadata.yaml (+9/-2)
reactive/prometheus-blackbox-exporter.py (+167/-11)
wheelhouse.txt (+2/-0)
Reviewer Review Type Date Requested Status
Alvaro Uria (community) Disapprove
BootStack Reviewers mr tracking; do not claim Pending
BootStack Reviewers Pending
BootStack Reviewers Pending
Canonical IS Reviewers Pending
Canonical IS Reviewers Pending
Review via email: mp+372592@code.launchpad.net

This proposal supersedes a proposal from 2019-09-06.

Commit message

Blackbox exporter rewritten as subordinate

To post a comment you must log in.
Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : Posted in a previous version of this proposal

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

This merge proposal is being monitored by mergebot. Change the status to Approved to merge.

Revision history for this message
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote :

Unable to determine commit message from repository - please click "Set commit message" and enter the commit message manually.

7927384... by Andrea Ieri

Add the juju-info interface explicitly

413057f... by Andrea Ieri

Publish the external IP in case we're running cross-model relations

64d1422... by Andrea Ieri

Filter out uninteresting IPs and interfaces

881b419... by Andrea Ieri

Lookup source ips for all probes and provide the address over the relation

a458c6a... by Andrea Ieri

Noop readability refactor

Revision history for this message
Alvaro Uria (aluria) wrote :

A new charm needs to be created (ie. prometheus-blackbox-peer-exporter) for this MP. The current charm is non-subordinate and used by IS, while the current fix makes the charm a subordinate.

I will talk to JL to create that new charm and create MPs against it.

review: Disapprove

Unmerged commits

a458c6a... by Andrea Ieri

Noop readability refactor

881b419... by Andrea Ieri

Lookup source ips for all probes and provide the address over the relation

64d1422... by Andrea Ieri

Filter out uninteresting IPs and interfaces

413057f... by Andrea Ieri

Publish the external IP in case we're running cross-model relations

7927384... by Andrea Ieri

Add the juju-info interface explicitly

f631577... by Andrea Ieri

Fix subordinate endpoint name

af6e21c... by Andrea Ieri

Cosmetic changes to layer/metadata. Make charm proof a little happier.

4bd9ed2... by Andrea Ieri

Add a wheelhouse.txt to cover python dependencies

60d1957... by Diko Parvanov

Added icmp checks and fixed hooks/peer relations

347f56e... by Diko Parvanov

Added multiple networks checks + MTU config option

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/config.yaml b/config.yaml
index d1dc24d..fc04ec0 100644
--- a/config.yaml
+++ b/config.yaml
@@ -16,6 +16,13 @@ options:
16 icmp:16 icmp:
17 prober: icmp17 prober: icmp
18 timeout: 10s18 timeout: 10s
19 icmp:
20 preferred_ip_protocol: "ip4"
21 payload_size: 1472
19 type: string22 type: string
20 description: |23 description: |
21 Blackbox exporter configuratin in raw YAML format24 Blackbox exporter configuratin in raw YAML format
25 scrape-interval:
26 type: string
27 default: "60s"
28 description: Set the blackbox exporter scrape jobs custom interval.
diff --git a/layer.yaml b/layer.yaml
index ddd95fc..6036e6d 100644
--- a/layer.yaml
+++ b/layer.yaml
@@ -1,6 +1,11 @@
1includes: ['layer:basic', 'interface:http', 'layer:snap']1includes:
2 - 'layer:basic'
3 - "interface:juju-info"
4 - 'interface:http'
5 - 'interface:peer-discovery'
2repo: 'https://git.launchpad.net/prometheus-blackbox-exporter-charm'6repo: 'https://git.launchpad.net/prometheus-blackbox-exporter-charm'
3ignore: ['.*.swp' ]7ignore:
8 - '.*.swp'
4options:9options:
5 basic:10 basic:
6 use_venv: true11 use_venv: true
diff --git a/metadata.yaml b/metadata.yaml
index 4f9228b..89d3c6e 100644
--- a/metadata.yaml
+++ b/metadata.yaml
@@ -1,4 +1,5 @@
1name: prometheus-blackbox-exporter1name: prometheus-blackbox-exporter
2display-name: Prometheus Blackbox Exporter
2summary: Blackbox exporter for Prometheus3summary: Blackbox exporter for Prometheus
3maintainer: Jacek Nykis <jacek.nykis@canonical.com>4maintainer: Jacek Nykis <jacek.nykis@canonical.com>
4description: |5description: |
@@ -7,9 +8,15 @@ description: |
7tags:8tags:
8 - monitoring9 - monitoring
9series:10series:
10 - xenial
11 - bionic11 - bionic
12subordinate: false12subordinate: true
13provides:13provides:
14 blackbox-exporter:14 blackbox-exporter:
15 interface: http15 interface: http
16requires:
17 general-info:
18 interface: juju-info
19 scope: container
20peers:
21 blackbox-peer:
22 interface: peer-discovery
diff --git a/reactive/prometheus-blackbox-exporter.py b/reactive/prometheus-blackbox-exporter.py
index a5d792a..7d4e877 100644
--- a/reactive/prometheus-blackbox-exporter.py
+++ b/reactive/prometheus-blackbox-exporter.py
@@ -1,19 +1,33 @@
1import ast
2import re
3import subprocess
1import yaml4import yaml
5import sys
26
3from charmhelpers.core import host, hookenv7from charmhelpers.core import host, hookenv
4from charmhelpers.core.templating import render8from charmhelpers.core.templating import render
5from charms.reactive import (9from charms.reactive import (
6 when, when_not, set_state, remove_state10 when, when_not, set_state, remove_state
7)11)
12from ipaddress import (
13 IPv4Interface, IPv4Address
14)
15from pyroute2 import IPRoute
16from netifaces import interfaces, ifaddresses, AF_INET
8from charms.reactive.helpers import any_file_changed, data_changed17from charms.reactive.helpers import any_file_changed, data_changed
9from charms.layer import snap18from charms.reactive import hook
19
20from charmhelpers.fetch import apt_install
1021
22hooks = hookenv.Hooks()
1123
12SNAP_NAME = 'prometheus-blackbox-exporter'24APT_PKG_NAME = 'prometheus-blackbox-exporter'
13SVC_NAME = 'snap.prometheus-blackbox-exporter.daemon'25SVC_NAME = 'prometheus-blackbox-exporter'
26EXECUTABLE = '/usr/bin/prometheus-blackbox-exporter'
14PORT_DEF = 911527PORT_DEF = 9115
15BLACKBOX_EXPORTER_YML_TMPL = 'blackbox.yaml.j2'28BLACKBOX_EXPORTER_YML_TMPL = 'blackbox.yaml.j2'
16CONF_FILE_PATH = '/var/snap/prometheus-blackbox-exporter/current/blackbox.yml'29CONF_FILE_PATH = '/etc/prometheus/blackbox.yml'
30IFACE_BLACKLIST_PATTERN = re.compile('^(lo|virbr|docker|lxdbr|vhost|tun|tap)')
1731
1832
19def templates_changed(tmpl_list):33def templates_changed(tmpl_list):
@@ -24,8 +38,9 @@ def templates_changed(tmpl_list):
24def install_packages():38def install_packages():
25 hookenv.status_set('maintenance', 'Installing software')39 hookenv.status_set('maintenance', 'Installing software')
26 config = hookenv.config()40 config = hookenv.config()
27 channel = config.get('snap_channel', 'stable')41 apt_install(APT_PKG_NAME, fatal=True)
28 snap.install(SNAP_NAME, channel=channel, force_dangerous=False)42 cmd = ["sudo", "setcap", "cap_net_raw+ep", EXECUTABLE]
43 subprocess.check_output(cmd)
29 set_state('blackbox-exporter.installed')44 set_state('blackbox-exporter.installed')
30 set_state('blackbox-exporter.do-check-reconfig')45 set_state('blackbox-exporter.do-check-reconfig')
3146
@@ -86,9 +101,150 @@ def restart_blackbox_exporter():
86 set_state('blackbox-exporter.started')101 set_state('blackbox-exporter.started')
87 remove_state('blackbox-exporter.do-restart')102 remove_state('blackbox-exporter.do-restart')
88103
89
90# Relations104# Relations
91@when('blackbox-exporter.started')105@hook('blackbox-peer-relation-{joined,departed}')
92@when('blackbox-exporter.available') # Relation name is "blackbox-exporter"106def configure_blackbox_exporter_relation(peers):
93def configure_blackbox_exporter_relation(target):107 hookenv.log('Running blackbox exporter relation.')
94 target.configure(PORT_DEF)108 hookenv.status_set('maintenance', 'Configuring blackbox peer relations.')
109 config = hookenv.config()
110
111 icmp_targets = []
112 tcp_targets = []
113 networks = []
114 for rid in hookenv.relation_ids('blackbox-peer'):
115 for unit in hookenv.related_units(rid):
116 unit_ports = hookenv.relation_get('unit-ports', rid=rid, unit=unit)
117 principal_unit = hookenv.relation_get('principal-unit', rid=rid, unit=unit)
118 unit_networks = hookenv.relation_get('unit-networks', rid=rid, unit=unit)
119 if unit_networks is not None:
120 unit_networks = ast.literal_eval(unit_networks)
121 for unit_network in unit_networks:
122 # Chcek if same network exists on this unit
123 if unit_network['net'] in [net['net'] for net in get_unit_networks()]:
124 networks.append(unit_network['net'])
125 probe_dst_ip = unit_network['ip']
126 icmp_targets.append({
127 'network': unit_network['net'],
128 'interface': unit_network['iface'],
129 'ip-address': probe_dst_ip,
130 'principal-unit': principal_unit,
131 'module': 'icmp',
132 'source-ip': _get_source_ip(probe_dst_ip)
133 })
134
135 if unit_ports is not None:
136 unit_ports = ast.literal_eval(unit_ports)
137 for port in unit_ports:
138 tcp_targets.append({
139 'ip-address': unit_network['ip'],
140 'port': port,
141 'principal-unit': principal_unit,
142 'module': 'tcp_connect',
143 })
144
145 relation_settings = {}
146 relation_settings['icmp_targets'] = icmp_targets
147 relation_settings['tcp_targets'] = tcp_targets
148 relation_settings['networks'] = networks
149 relation_settings['ip_address'] = hookenv.unit_get('private-address')
150 relation_settings['port'] = PORT_DEF
151 relation_settings['job_name'] = hookenv.principal_unit()
152 relation_settings['scrape_interval'] = config.get('scrape-interval')
153
154
155 for rel_id in hookenv.relation_ids('blackbox-exporter'):
156 relation_settings['ip_address'] = \
157 hookenv.ingress_address(rid=rel_id, unit=hookenv.local_unit())
158 hookenv.relation_set(relation_id=rel_id, relation_settings=relation_settings)
159
160 hookenv.status_set('active', 'Ready')
161
162
163def _get_source_ip(destination):
164 """
165 Get the source ip of a connection towards destination without having to run
166 ip r g via subprocess
167
168 Disclaimer: source ip configuration for the blackbox exporter is done in
169 the module definition. To avoid having to create a module for every local
170 address we are simply letting the exporter use whatever source IP the
171 kernel deems appropriate. Be aware that this function is doing a route
172 lookup, and it is not - strictly speaking - returning the source IP of the
173 packet the blackbox exporter will generate. The two addresses should always
174 be the same, but YMMV.
175 """
176 with IPRoute() as ipr:
177 routes = ipr.route('get', dst=destination)
178 return routes[0].get_attr('RTA_PREFSRC')
179
180
181def _is_valid_ip(address):
182 """
183 Filter out "uninteresting" addresses
184 """
185 ip = IPv4Address(address.get('addr'))
186 return not (ip.is_multicast or
187 ip.is_reserved or
188 ip.is_link_local or
189 ip.is_loopback)
190
191
192def _is_valid_iface(iface):
193 """
194 Ignore interfaces used by Docker, KVM, Contrail, etc
195 """
196 if IFACE_BLACKLIST_PATTERN.search(iface):
197 return False
198 else:
199 return True
200
201
202def get_unit_networks():
203 networks = []
204 for iface in filter(_is_valid_iface, interfaces()):
205 ip_addresses = ifaddresses(iface)
206 for ip_address in filter(_is_valid_ip, ip_addresses.get(AF_INET, [])):
207 ip_v4 = IPv4Interface(
208 "{}/{}".format(ip_address['addr'], ip_address['netmask'])
209 )
210 networks.append(
211 {"iface": iface,
212 "ip": str(ip_v4.ip),
213 "net": str(ip_v4.network)}
214 )
215 return networks
216
217def get_principal_unit_open_ports():
218 cmd = "lsof -P -iTCP -sTCP:LISTEN".split()
219 result = subprocess.check_output(cmd)
220 result = result.decode(sys.stdout.encoding)
221
222 ports = []
223 for r in result.split('\n'):
224 for p in r.split():
225 if '*:' in p:
226 ports.append(p.split(':')[1])
227 ports = [p for p in set(ports)]
228
229 return ports
230
231@hook('blackbox-peer-relation-{joined,departed}')
232def blackbox_peer_departed(peers):
233 hookenv.log('Blackbox peer unit joined/departed.')
234 set_state('blackbox-exporter.redo-peer-relation')
235
236@when('blackbox-exporter.redo-peer-relation')
237def setup_blackbox_peer_relation(peers):
238 # Set blackbox-peer relations
239 hookenv.log('Running blackbox peer relations.')
240 hookenv.status_set('maintenance', 'Configuring blackbox peer relations.')
241 for rid in hookenv.relation_ids('blackbox-peer'):
242 relation_settings = hookenv.relation_get(rid=rid, unit=hookenv.local_unit())
243 relation_settings['principal-unit'] = hookenv.principal_unit()
244 relation_settings['private-address'] = hookenv.unit_get('private-address')
245 relation_settings['unit-networks'] = get_unit_networks()
246 relation_settings['unit-ports'] = get_principal_unit_open_ports()
247 hookenv.relation_set(relation_id=rid, relation_settings=relation_settings)
248
249 hookenv.status_set('active', 'Ready')
250 remove_state('blackbox-exporter.redo-peer-relation')
diff --git a/wheelhouse.txt b/wheelhouse.txt
95new file mode 100644251new file mode 100644
index 0000000..ea2fb5e
--- /dev/null
+++ b/wheelhouse.txt
@@ -0,0 +1,2 @@
1netifaces
2pyroute2

Subscribers

People subscribed via source and target branches