[Python 2.7] CSV naar InfluxDB zonder requests

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • significant
  • Registratie: Juni 2008
  • Laatst online: 21:33
Mijn vraag

Graag wil ik de internetsnelheid monitoren en deze opslaan in een InfluxDB. Hiervoor gebruik ik speedtest-cli (https://github.com/sivel/speedtest-cli) op de EdgeRouter. Dit geeft mij de data die ik wil hebben.

Nu moet dit naar InfluxDB, hiervoor kun je gebruik maken van CURL. Voorbeeld o.a. op https://docs.influxdata.com/influxdb/v1.8/guides/write_data/

Echter, EdgeOS is uitgerust met een beperkte Python 2.7 omgeving en andere packages installeren is niet mogelijk. Als alternatief voor "Requests" wijk ik uit naar URLLIB2.

Gemakshalve heb ik de resultaten in CSV beschikbaar om het volgende te draaien:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python

import csv
from urllib import urlencode
import urllib2
import json

def read_csv(filename):
    with open(filename) as f:
        file_data=csv.reader(f)
        headers=next(file_data)
        return [dict(zip(headers,i)) for i in file_data]

url = "http://192.168.1.12:8086/write?db=ISPSpeed"

reqdata = {"measurements":read_csv("output.txt")[0]}

postData = json.dumps(reqdata )

cont_len = len(postData )
req = urllib2.Request(url, postData, {'Content-Type': 'application/json', 'Content-Length': cont_len})
f = urllib2.urlopen(req)
response = f.read()
f.close()


Dit geeft echter fout 400 "Bad Request". Als ik de URL open krijg ik "Method not allowed"

Relevante software en hardware die ik gebruik
EdgeRouter met EdgeOS en Python 2.7 in een kale variant.

Wat zie ik over het hoofd waarom het niet werken wil?

Beste antwoord (via significant op 20-09-2021 12:44)


  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Dat is niet zo lastig toch? Je kunt gewoon de waardes in een format string gooien vanuit de regels die je inleest uit de CSV. Je kunt bijvoorbeeld een template maken, waar je dan de waardes in plakt:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
# Uitgaande van CSV regel: server,region,value,timestamp
# Bijvoorbeeld: server02,us-west,0.55,1422568543702900257
line_tmpl = "cpu_load_short,server={0:s},region={1:s} value={2:f} {3:d}"

with open("server_metrics.csv", "r") as csv_file:
  reader = csv.reader(csv_file)

  # Get headers
  header = next(reader)
  
  # Format each line and join with newline
  line_data = "\n".join[line_tmpl.format(*line) for line in reader]

[ Voor 11% gewijzigd door Morrar op 26-08-2021 10:04 ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 22:11

Creepy

Tactical Espionage Splatterer

Ik denk dat je de csv file as is moet posten. read_csv is denk ik van pandas? Die geeft een dataframe terug. Je post dus nu niet de csv file zelf.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik heb geen ervaring met CSV's posten naar InfluxDB maar ik zou eens kijken naar het Line protocol. Dat is best eenvoudig. Dan loop je gewoon in Python over je CSV data en post je het zo.

[ Voor 16% gewijzigd door RobIII op 13-08-2021 17:16 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Die method not allowed krijg je omdat je een GET request doet als je de URL opent in een browser.

Bad request betekent dat je request een fout bevat; wellicht heeft de data de verkeerde structuur of bestaat de database niet?

Wat je kan doen is een programma als Postman installeren en dan zelf een POST request met data doen naar dat URL. Construeer dan zelf wat nepdata totdat het werkt.

Stap 2 is dan die werkende nepdata in je Python script stoppen, totdat je het daar ook werkend hebt. Dan weet je in ieder geval dat de data goed is.

Stap 3 is dan de data uit de CSV in hetzelfde format als de nepdata te krijgen. Waarschijnlijk verwacht je endpoint een bepaalde structuur qua JSON.

Zo stop je nu direct het eerste record als dict achter "measurements" in de JSON maar wellicht verwacht InfluxDB daar een lijst van dicts / records?

Hoe dan ook een tool als Postman geeft je meer mogelijkheden om te experimenteren met je API endpoint.

[ Voor 4% gewijzigd door Morrar op 13-08-2021 17:51 ]


Acties:
  • 0 Henk 'm!

  • Kalentum
  • Registratie: Juni 2004
  • Laatst online: 21:21
Het is hier zelf programmeren, maar er is een oplossing die van Telegraph gebruik maakt

https://grafana.com/grafana/dashboards/1756

Acties:
  • +1 Henk 'm!

  • significant
  • Registratie: Juni 2008
  • Laatst online: 21:33
Het heeft even stil gelegen ivm tijd gebrek, er is een oplossingsrichting. Eerst antwoord op de reacties.
Creepy schreef op vrijdag 13 augustus 2021 @ 16:57:
Ik denk dat je de csv file as is moet posten. read_csv is denk ik van pandas? Die geeft een dataframe terug. Je post dus nu niet de csv file zelf.
Nee, geen Panda's een zelf gedefineerde functie (zie ook de bijgesloten code)
RobIII schreef op vrijdag 13 augustus 2021 @ 17:14:
Ik heb geen ervaring met CSV's posten naar InfluxDB maar ik zou eens kijken naar het Line protocol. Dat is best eenvoudig. Dan loop je gewoon in Python over je CSV data en post je het zo.
Daar lijkt een deel van het probleem te zitten, dank voor de suggestie.
Morrar schreef op vrijdag 13 augustus 2021 @ 17:49:
Die method not allowed krijg je omdat je een GET request doet als je de URL opent in een browser.

Bad request betekent dat je request een fout bevat; wellicht heeft de data de verkeerde structuur of bestaat de database niet?

Wat je kan doen is een programma als Postman installeren en dan zelf een POST request met data doen naar dat URL. Construeer dan zelf wat nepdata totdat het werkt.

Stap 2 is dan die werkende nepdata in je Python script stoppen, totdat je het daar ook werkend hebt. Dan weet je in ieder geval dat de data goed is.

Stap 3 is dan de data uit de CSV in hetzelfde format als de nepdata te krijgen. Waarschijnlijk verwacht je endpoint een bepaalde structuur qua JSON.

Zo stop je nu direct het eerste record als dict achter "measurements" in de JSON maar wellicht verwacht InfluxDB daar een lijst van dicts / records?

Hoe dan ook een tool als Postman geeft je meer mogelijkheden om te experimenteren met je API endpoint.
Postman heeft geholpen om het probleem te vinden maar ook de richting naar de oplossing. Mooie tool, dank voor deze suggestie.
Kalentum schreef op vrijdag 13 augustus 2021 @ 18:07:
Het is hier zelf programmeren, maar er is een oplossing die van Telegraph gebruik maakt

https://grafana.com/grafana/dashboards/1756
Deze had ik ook gezien maar voegt veel "ballast" toe wat ik niet gebruik


Mbv Postman heb ik uitgevonden dat mijn request niet goed is, ik formatteer het Line Protocol niet goed. Specifiek mis ik quotes om alles wat een string is.
Ik probeer dit nu te verweven in de huidige code, suggesties welkom

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Als het goed is zorgt json.dumps() voor de quotes om string waardes heen toch? Dus dat zou het probleem niet mogen zijn; of verwacht InfuxDB ook quotes om numerieke waardes?

  • Kontsnorretje
  • Registratie: Augustus 2011
  • Laatst online: 14-06-2024
Morrar schreef op donderdag 19 augustus 2021 @ 18:18:
Als het goed is zorgt json.dumps() voor de quotes om string waardes heen toch? Dus dat zou het probleem niet mogen zijn; of verwacht InfuxDB ook quotes om numerieke waardes?
Nee, want de read_csv wordt rechtstreeks in de string geplakt. Hierdoor bouw je eigenlijk zelf een JSON string, en heb je geen invloed op het eventueel escapen van een waarde.

Wat @significant zou moeten (kunnen, er zijn meer wegen naar Rome) doen, is in Python een dictionary maken, die dezelfde structuur heeft als de data die hij in JSON formaat wil hebben. Hier kun je vervolgens de waardes middels een for/while loop van de CSV in plaatsen.

Door deze dict aan json.dumps mee te geven, zorgt die methode voor een nette output die in het request meegegeven kan worden.

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Kontsnorretje schreef op donderdag 19 augustus 2021 @ 22:14:
[...]


Nee, want de read_csv wordt rechtstreeks in de string geplakt. Hierdoor bouw je eigenlijk zelf een JSON string, en heb je geen invloed op het eventueel escapen van een waarde.

Wat @significant zou moeten (kunnen, er zijn meer wegen naar Rome) doen, is in Python een dictionary maken, die dezelfde structuur heeft als de data die hij in JSON formaat wil hebben. Hier kun je vervolgens de waardes middels een for/while loop van de CSV in plaatsen.

Door deze dict aan json.dumps mee te geven, zorgt die methode voor een nette output die in het request meegegeven kan worden.
Nee de read_csv retourneert een list met daarin een dict voor iedere regel in het CSV bestand, zie regel 12:

Python:
1
return [dict(zip(headers, i)) for i in data_file]


Die i krijgt een list met waardes voor iedere regel uit het CSV bestand en die waardes worden met de headers gecombineerd tot een dict.

Vervolgens wordt alleen het eerste dict uit deze lijst weer in een ander dict gestopt:

Python:
1
reqdata = {"measurements": read_csv("output.txt")[0]}


In feite is dit equivalent aan:

Python:
1
reqdata = {"measurements": {"header1": "value1", "header2": "value2", ..., "headerN": "valueN"}}


En dat reqdata dict gaat dus in de json.dumps(). Kortom json.dumps() krijgt wel degelijk een dict als input en zou strings daarin moeten quoten...

[ Voor 5% gewijzigd door Morrar op 19-08-2021 22:27 ]


  • Kontsnorretje
  • Registratie: Augustus 2011
  • Laatst online: 14-06-2024
Morrar schreef op donderdag 19 augustus 2021 @ 22:23:
[...]


Nee de read_csv retourneert een list met daarin een dict voor iedere regel in het CSV bestand, zie regel 12:

Python:
1
return [dict(zip(headers, i)) for i in data_file]


Die i krijgt een list met waardes voor iedere regel uit het CSV bestand en die waardes worden met de headers gecombineerd tot een dict.

Vervolgens wordt alleen het eerste dict uit deze lijst weer in een ander dict gestopt:

Python:
1
reqdata = {"measurements": read_csv("output.txt")[0]}


In feite is dit equivalent aan:

Python:
1
reqdata = {"measurements": {"header1": "value1", "header2": "value2", ..., "headerN": "valueN"}}


En dat reqdata dict gaat dus in de json.dumps(). Kortom json.dumps() krijgt wel degelijk een dict als input en zou strings daarin moeten quoten...
Je hebt gelijk. Maar het zal wel op regel 12 zitten. Die oneliner uitsplitsen maakt debuggen makkelijker.

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Als ik overigens de gelinkte API docs bekijk, dan zie niet dat de API JSON accepteert; er wordt alleen ruwe tekst data ondersteunt in LineProtocol format. Dat ziet er ongeveer zo uit:

code:
1
2
3
cpu_load_short,host=server02 value=0.67
cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257
cpu_load_short,direction=in,host=server01,region=us-west value=2.0 1422568543702900257


Dus meer algemeen:
code:
1
<measurement_name>,<tag=value>,...,<tag=value> <field>=<value> <timestamp>


Dus:
1. Naam meting
2. Een comma.
3. Een of meer tags als tag=value gescheiden door comma's.
4. Een spatie (!).
5. Veldnaam en meetwaarde als field=value.
6. Nog een spatie (!).
7. Een (optionele?) timestamp.

Ik vermoed dus eerder dat de TS zijn data in dat formaat moet gieten en daarna moet posten als ruwe data (dus content type veranderen van application/json naar bijvoorbeeld 'application/octet-stream')

[ Voor 10% gewijzigd door Morrar op 19-08-2021 22:57 ]


Acties:
  • 0 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 20:07
Waarom deze omweg via csv files?
Die speedtest-cli is in python geschreven en op github als source beschikbaar.
Haal daar alleen de python code uit die de echte speedtest doet en schrijf de data weg in die database.

Zo heb je enkel een python script nodig en geen speedtest-cli die csv files maakt, die je weer in python inleest en dan weer in een Influx database wegschrijft.

Ik weet niet waarom je een Influx database gebruikt, maar als je een keuze hebt zou ik persoonlijk Sqlite3 gebruiken, die zit al in python ingebakken.

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • 0 Henk 'm!

  • significant
  • Registratie: Juni 2008
  • Laatst online: 21:33
Dank voor de inzichten:
- Om speedtest-cli aan te passen zou ook een optie zijn, ik wilde echter proberen (eigenwijs/leerervaring) dit als input te zien en met iets te komen wat werkt.

Vergeten in mijn vorige post inderdaad te vermelden: ik had niet goed gelezen en moet inderdaad het Line format van InfluxDB aanhouden (dus geen JSON meesturen).

Uitdaging zit dus in het creeeren van een Lineformat, met daarin de juist gequote strings. Nu tijd om daar even mee aan de slag te gaan,

Acties:
  • Beste antwoord
  • 0 Henk 'm!

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 17:01
Dat is niet zo lastig toch? Je kunt gewoon de waardes in een format string gooien vanuit de regels die je inleest uit de CSV. Je kunt bijvoorbeeld een template maken, waar je dan de waardes in plakt:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
# Uitgaande van CSV regel: server,region,value,timestamp
# Bijvoorbeeld: server02,us-west,0.55,1422568543702900257
line_tmpl = "cpu_load_short,server={0:s},region={1:s} value={2:f} {3:d}"

with open("server_metrics.csv", "r") as csv_file:
  reader = csv.reader(csv_file)

  # Get headers
  header = next(reader)
  
  # Format each line and join with newline
  line_data = "\n".join[line_tmpl.format(*line) for line in reader]

[ Voor 11% gewijzigd door Morrar op 26-08-2021 10:04 ]


Acties:
  • 0 Henk 'm!

  • significant
  • Registratie: Juni 2008
  • Laatst online: 21:33
Dank @Morrar . Hiermee ben ik verder gekomen.
Pagina: 1