2626 SCRAPE_INTERVAL = 30
2727
2828
29- HETZNER_CLOUD_API_URL = 'https://api.hetzner.cloud/v1/load_balancers/'
29+ HETZNER_CLOUD_API_URL_BASE = 'https://api.hetzner.cloud/v1'
30+ HETZNER_CLOUD_API_URL_LB = f'{ HETZNER_CLOUD_API_URL_BASE } /load_balancers/'
31+ HETZNER_CLOUD_API_URL_SERVER = f'{ HETZNER_CLOUD_API_URL_BASE } /servers/'
3032
31- def get_all_load_balancers_ids ():
32- url = f'{ HETZNER_CLOUD_API_URL } '
33+
34+ def get_all_load_balancers_ids () -> dict :
35+ url = f'{ HETZNER_CLOUD_API_URL_LB } '
3336 headers = {
3437 'Content-type' : "application/json" ,
3538 'Authorization' : f"Bearer { access_token } "
@@ -39,8 +42,8 @@ def get_all_load_balancers_ids():
3942 return get .json ()['load_balancers' ]
4043
4144
42- def get_load_balancer_info (lbid ):
43- url = f'{ HETZNER_CLOUD_API_URL } { lbid } '
45+ def get_load_balancer_info (lbid ) -> dict :
46+ url = f'{ HETZNER_CLOUD_API_URL_LB } { lbid } '
4447
4548 headers = {
4649 'Content-type' : "application/json" ,
@@ -51,12 +54,51 @@ def get_load_balancer_info(lbid):
5154 return get .json ()
5255
5356
57+ def get_all_server_names () -> dict :
58+ url = f'{ HETZNER_CLOUD_API_URL_SERVER } '
59+
60+ headers = {
61+ 'Content-type' : "application/json" ,
62+ 'Authorization' : f"Bearer { access_token } "
63+ }
64+
65+ get = requests .get (url , headers = headers )
66+ return {x ['id' ]: x ['name' ] for x in get .json ()['servers' ]}
67+
68+
69+ def get_server_info (server_id ) -> dict :
70+ url = f'{ HETZNER_CLOUD_API_URL_SERVER } { server_id } '
71+
72+ headers = {
73+ 'Content-type' : "application/json" ,
74+ 'Authorization' : f"Bearer { access_token } "
75+ }
76+
77+ get = requests .get (url , headers = headers )
78+ return get .json ()
79+
80+
81+ def get_server_name_from_cache (server_id : str ) -> str :
82+ global server_name_cache
83+ if server_id in server_name_cache :
84+ return server_name_cache [server_id ]
85+ else :
86+ # Refresh cache
87+ server_name_cache = get_all_server_names ()
88+ # Check again
89+ if server_id in server_name_cache :
90+ return server_name_cache [server_id ]
91+ else :
92+ # If still not found, return id as name
93+ return server_id
94+
95+
5496def get_metrics (metrics_type , lbid ):
5597 utc_offset_sec = time .altzone if time .localtime ().tm_isdst else time .timezone
5698 utc_offset = datetime .timedelta (seconds = - utc_offset_sec )
5799 hetzner_date = datetime .datetime .now ().replace (tzinfo = datetime .timezone (offset = utc_offset )).isoformat ()
58100
59- url = f"{ HETZNER_CLOUD_API_URL } { lbid } /metrics"
101+ url = f"{ HETZNER_CLOUD_API_URL_LB } { lbid } /metrics"
60102
61103 headers = {
62104 'Content-type' : "application/json" ,
@@ -95,7 +137,7 @@ def get_metrics(metrics_type, lbid):
95137 try :
96138 load_balancer_name = load_balancer ['name' ]
97139 except Exception as e :
98- print ('Couldnt get field' , e )
140+ print ("Couldn't get field" , e )
99141 sys .exit (1 )
100142
101143
@@ -111,18 +153,24 @@ def get_metrics(metrics_type, lbid):
111153
112154 print (f'\n Scrape intreval: { SCRAPE_INTERVAL } seconds' )
113155
156+ print ('\n Building server name cache from Hetzner for labeling ...' )
157+ server_name_cache = get_all_server_names ()
158+ print (f'Retrieved { len (server_name_cache .keys ())} server names from Hetzner ...\n ' )
159+
114160 id_name_list = ['hetzner_load_balancer_id' , 'hetzner_load_balancer_name' ]
115161 hetzner_load_balancer_info = Info ('hetzner_load_balancer' , 'Hetzner Load Balancer Exporter build info' )
116162 hetzner_openconnections = Gauge ('hetzner_load_balancer_open_connections' , 'Open Connections on Hetzner Load Balancer' , id_name_list )
117163 hetzner_connections_per_second = Gauge ('hetzner_load_balancer_connections_per_second' , 'Connections per Second on Hetzner Load Balancer' , id_name_list )
118164 hetzner_requests_per_second = Gauge ('hetzner_load_balancer_requests_per_second' , 'Requests per Second on Hetzner Load Balancer' , id_name_list )
119165 hetzner_bandwidth_in = Gauge ('hetzner_load_balancer_bandwidth_in' , 'Bandwidth in on Hetzner Load Balancer' , id_name_list )
120166 hetzner_bandwidth_out = Gauge ('hetzner_load_balancer_bandwidth_out' , 'Bandwidth out on Hetzner Load Balancer' , id_name_list )
167+ id_name_service_list = id_name_list + ['hetzner_target_id' , 'hetzner_target_name' , 'hetzner_target_port' ]
168+ hetzner_service_state = Gauge ('hetzner_load_balancer_service_state' , 'Health status of Load Balancer\' s services' , id_name_service_list )
121169
122170 start_http_server (8000 )
123171 print ('\n Hetzner Load Balancer Exporter started' )
124172 print ('Visit http://localhost:8000/ to view the metrics' )
125- hetzner_load_balancer_info .info ({'version' : '2.0.0' , 'buildhost' : 'drake0103 @gmail.com' })
173+ hetzner_load_balancer_info .info ({'version' : '2.0.0' , 'buildhost' : 'netblognet @gmail.com' })
126174
127175 while True :
128176 for load_balancer_id , lb_name , load_balancer_type in load_balancer_full_list :
@@ -137,4 +185,14 @@ def get_metrics(metrics_type, lbid):
137185 hetzner_load_balancer_name = lb_name ).set (get_metrics ('bandwidth' ,load_balancer_id )["metrics" ]["time_series" ]["bandwidth.in" ]["values" ][0 ][1 ])
138186 hetzner_bandwidth_out .labels (hetzner_load_balancer_id = load_balancer_id ,
139187 hetzner_load_balancer_name = lb_name ).set (get_metrics ('bandwidth' ,load_balancer_id )["metrics" ]["time_series" ]["bandwidth.out" ]["values" ][0 ][1 ])
188+
189+ lb_info = get_load_balancer_info (load_balancer_id )['load_balancer' ]
190+ for target in [x for x in lb_info ['targets' ] if x ['type' ] == 'server' ]:
191+ for health_status in target ['health_status' ]:
192+ hetzner_service_state .labels (hetzner_load_balancer_id = load_balancer_id ,
193+ hetzner_load_balancer_name = lb_name ,
194+ hetzner_target_id = target ['server' ]['id' ],
195+ hetzner_target_name = get_server_name_from_cache (target ['server' ]['id' ]),
196+ hetzner_target_port = health_status ['listen_port' ])\
197+ .set ((1 if health_status ['status' ] == 'healthy' else 0 ))
140198 time .sleep (int (SCRAPE_INTERVAL ))
0 commit comments