Inventarisierungstool für Client-Hardware

Hallo,

ich suche eine einfaches Inventarisierungstool um die Hardware der Clients automatisiert zu inventarisieren.
Wenn ein Client an ist, sollten automatisch einige Hardwaredaten dokumentiert werden an denen ich z.B. Hersteller und Produkttyp erkennen kann, sowie CPU, RAM, Clientname…

Ich habe nur Linuxclients. Windows habe ich überhaupt nicht mehr im Einsatz.
Am liebsten wäre mir daher etwas, was unter Linux läuft, z.B. direkt auf dem LMN-Server.
Es muss also keine grafische Oberfläche sein - als Ausgabe würde mir z.B. eine Datei reichen, die ich in LibreOffice Calc importieren kann.

Hat jemand etwas Ähnliches im Einsatz?

Gruß,
Stefan

Hallo Stefan,

ich suche eine einfaches Inventarisierungstool um die Hardware der
Clients automatisiert zu inventarisieren.
Wenn ein Client an ist, sollten automatisch einige Hardwaredaten
dokumentiert werden an denen ich z.B. Hersteller und Produkttyp erkennen
kann, sowie CPU, RAM, Clientname…

Ich habe nur Linuxclients. Windows habe ich überhaupt nicht mehr im Einsatz.
Am liebsten wäre mir daher etwas, was unter Linux läuft, z.B. direkt auf
dem LMN-Server.
Es muss also keine grafische Oberfläche sein - als Ausgabe würde mir
z.B. eine Datei reichen, die ich in LibreOffice Calc importieren kann.

reicht da nicht einfach das Absetzen von
lspci
und
cat /proc/cpuinfo

das kannst du ja vom server aus machen mittels
ssh root@cleintname lspci

Das ganze automatisieren und zurechtschneiden … voila :slight_smile:

free
gibt dir noch den Hauptspeicher aus …

df -h
die gemounteten Partitionen mit Platz …

oder ein
fdisk -l
gibt alle angeschlossenen Massenspeicher raus.

LG

Holger

Hallo zusammen,

da ich in der Richtung nichts Fertiges gefunden habe, habe ich etwas zusammengestellt im unten angehängten, auf einem Linux-Client lauffähigen aber unfertigen Skript.

@Holger: Danke für die Anregungen!

Gerne würde ich die Infos der Clients in einer csv-Datei auf dem Server zusammenführen:

Jetzt bräuchte ich noch Eure Hilfe: Wie könnte man denn geschickt die auf dem Client gesammelten Infos auf dem Server in die csv-Datei schreiben???

Gruß,

Stefan

#!/bin/bash

# UNFERTIG-Skript linuxmuster.net 7.0
# _seninv
#
# zugehörige Datei:
# UNFERTIG
#
# Sammelt Systeminformationen und UNFERTIG
#
# Linux Mint 20.3 Xfce 64bit
# 2022-09-27
# GPL v3
# Sen

###Zeitstempel
Zeit=$(date +%F___%T)

###Hostname
Host=$(hostname | sed '{s/;/,/g}')

###Herstellerinfo
#Marke
Marke=$(sudo inxi -M | sed -ne '/^Machine/{s/.*System: //;s/ product.*//;p;q;}' | sed '{s/;/,/g}')
#Typ
Typ=$(sudo inxi -M | sed -ne '/^Machine/{s/.*product: //;s/ v:.*//;p;q;}' | sed '{s/;/,/g}')
#Seriennummer
Sno=$(sudo inxi -M | sed -ne '/^Machine/{s/.*serial: //;s/ .*//;p;q;}' | sed '{s/;/,/g}')

###MAC
#WiFi
MACwifi=$(iw dev | grep addr | awk '{print $2}' | sed '{s/;/,/g}')
#Cable
if [[ "$MACwifi" == "" ]]; then  
MACcable=$(ip addr | sed -ne '/link\/ether/{s/.*ether //;s/ .*//;p;}')
else
MACcable=$(ip addr | sed -ne '/link\/ether/{s/.*ether //;s/ .*//;p;}' | sed "/$MACwifi/d")
fi

###CPU
Cpu=$(sudo inxi -C | sed -ne '/^CPU:/{s/.*model: //;s/ bits.*//;p;q;}' | sed '{s/;/,/g}')

###RAM:
#Typ
sudo dmidecode -t memory | sed -ne '/DDR/{s/.*Type: //;s/ .*//;p;}' | uniq | sed '{s/;/,/g}' > /tmp/seninv-ramtype
readarray -t ramtype < /tmp/seninv-ramtype
RAMtype=$(echo ${ramtype[*]})
#Module
sudo dmidecode -t memory | sed -ne '/Size/{s/.*Size: //;s/ .*//;p;}' | sort | sed '{s/;/,/g}' > /tmp/seninv-rammodules
readarray -t rammodules < /tmp/seninv-rammodules
RAMmodules=$(echo ${rammodules[*]})
#RAMgesamt
RAMgesamt=$(awk '{sum+=$1}END{print sum}' /tmp/seninv-rammodules)

###Festplatte
#Größe
DISKsize=$(sudo inxi -D | sed -ne '/vendor:/{s/.*size: //;s/ .*//;p;q;}' | sed '{s/;/,/g}')
#Marke
DISKmarke=$(sudo inxi -D | sed -ne '/vendor:/{s/.*vendor: //;s/ model:.*//;p;q;}' | sed '{s/;/,/g}')
#Modell
DISKmodell=$(sudo inxi -D | sed -ne '/vendor:/{s/.*model: //;s/ size:.*//;p;q;}' | sed '{s/;/,/g}')

###Netzwerkkarte
LANkarte=$(sudo inxi -N | sed -ne '/^Network:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')

###Grafik
Grafik=$(sudo inxi -G | sed -ne '/^Graphics:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')

###Audio
Audio=$(sudo inxi -A | sed -ne '/^Audio:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')


###PCI
sudo lspci | sed '{s/;/,/g}' > /tmp/seninv-pci
readarray -t pci < /tmp/seninv-pci
PCIinfo=$(echo ${pci[*]})



###Ausgabe in csv
echo "Zeit;Hostname;MAC-Cable;MAC-WiFi;Marke;Typ;SerienNr;CPU;RAMtyp;RAMgesamt(MB);RAMmodule(MB);DISKsize(GB);DISKmarke;DISKmodell;LANkarte;Grafik;Audio;;PCI" > /tmp/info.csv
echo "$Zeit;$Host;$MACcable;$MACwifi;$Marke;$Typ;$Sno;$Cpu;$RAMtype;$RAMgesamt;$RAMmodules;$DISKsize;$DISKmarke;$DISKmodell;$LANkarte;$Grafik;$Audio;;$PCIinfo" >> /tmp/info.csv



###Aufräumen
rm -f /tmp/seninv*

PS: Hier ein Video, welches mich inspiriert hat:

Hallo. Warum nimmst Du nicht hwinfo?
Ich bin zwar gerade nicht sicher, ob das einen CSV Export kann, aber es sieht so aus:

Viele Grüße
Michael

Hi Stefan,
eine Möglichkeit wäre per ssh/scp mit Authentifizierung per key. Auf dem Client erzeugst Du für den User Root einen ssh-key (mit ssh-keygen). Auf dem Server einen User „hardwaresammler“ anlegen. In dessen Heimatordner den Unterordner .ssh anlegen, Rechte auf 700 setzen und erzeugst darin die Datei authorized_keys. In diese Datei kopierst Du den öffentlichen Schlüssel .ssh/id_rsa.pub des roots des Clients. Jetzt kann sich der root eines Clients ohne Passworteingabe beim Server anmelden.
Dein Skript schreibt seine Info in eine lokale Temporärdatei, sagen wir /tmp/hardware.csv. Die kopiert es anschließend auf den Server.

scp -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null /tmp/hardware.csv hardwaresammler@10.16.1.1:/tmp/neuehardware.csv

Dann lässt Du die neuen Daten in eine Sammeldatei laufen:

ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null hardwaresammler@10.16.1.1 'cat /tmp/neuehardware.csv >> ~/hardware.csv'

Quotes nicht vergessen…

Was da jetzt noch nicht drin ist, ist der Check, ob die gefundene Hardware schon in der Liste ist. Das könntest Du aber ggf. auch remote abfragen, mit sowas wie

count=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null hardwaresammler@10.16.1.1 'grep '$rechnerid' ~/hardware.csv |wc -l' )

Aber das ist vielleicht auch viel zu kompliziert.

Eventuell ist es fast leichter, mit python und flask einen Mini-REST-Schnittstelle zu schreiben und die dann aufzurufen.

Gruß
Sascha

Mögliche Alternative:
Auf dem Server sowas laufen lassen:

while true
do
   nc -l 34582 >> /tmp/neu.csv
   neuenEintragUeberpruefen()
done

„neuenEintragUeberpruefen()“ wäre eine noch zu schreibende Funktion, die überprüft, ob die Angaben in neu.csv schon in Deiner zentralen Datei stehen und ggf. hinein-"cat"ed.

Auf dem Client die Daten in einer Datei /tmp/neu.csv korrekt formatiert ablegen und mit

nc -N 10.16.1.1 34582 < /tmp/neu.csv

schicken. Nachteil dabei ist, dass es einen offenen Port gibt und keinerlei Passwortcheck.

Gruß
Sascha

Ich bin da etwas angefixt, weil ich sowas auch gerne hätte…deswegen hier ein mit der heißen Nadel gestrickter Beispielentwurf für eine Python-Lösung (ohne flask sondern etwas mehr low level mit Sockets)

Server

import socket,json

def read(conn):
    data = ""
    while conn:
        packet = conn.recv(1024).decode('utf-8')
        if not packet:
            break
        data += packet
    return data

def check(name,filename):
    try:
        file=open(filename,'r')
        for line in file:
            if line.startswith(name):
                return True
    except:
        pass
    return False

def store(sysinfo):
    infofile='hardware.csv'
    if not check(sysinfo['name'],infofile):
        out=open(infofile,'a')
        out.write(f'{sysinfo["name"]};{sysinfo["cpu"]};{sysinfo["disksize"]}\n')
        out.close()

HOST = "127.0.0.1"
PORT = 3791

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    while True:
        s.listen()
        conn, addr = s.accept()
        with conn:
            print(f"Connected by {addr}")
            data = read(conn)
            if data:
                store(json.loads(data))
        

Client

import platform,socket,os,json

def disksize(): ## in GB     
    ## ToDO
     return 5

def send(text):
    HOST = "127.0.0.1" 
    PORT = 3791
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((HOST, PORT))
        s.sendall(text.encode('utf-8'))
        s.close()

sysinfo=dict()
sysinfo['name']=socket.gethostname()
sysinfo['cpu']=platform.processor()
sysinfo['disksize'] = disksize()

send(json.dumps(sysinfo))

Hallo Sascha,

Danke für Deine Beiträge! Leider kann ich python nicht.

  • Der ssh-Zugriff vom Client auf den Server ist eine Lösungsmöglichkeit, die für mich auf jeden Fall in Frage kommt. Da ich mich damit noch nicht so gut auskenne, helfen mir Deine Ausführungen bestimmt! Geskriptet habe ich das heute noch nicht.

  • Ich würde das Clientskript dann direkt per ssh die Werte in die csv-Datei auf dem Server schreiben lassen. Es gäbe dann gar keine csv-datei auf dem Client.

  • Der Abgleich, ob die Hardware schon in der Liste ist, habe ich gerade noch geskriptet - das war nicht so kompliziert.

#Ausgabedatei mit Ueberschriften anlegen falls nötig
if ! [ -f ""$Ausgabedatei"" ]; then echo ""$Ueberschriften"" > ""$Ausgabedatei""; fi

#Falls MAC-Adresse in csv-Datei schon vorhanden,wird diese Wertezeile gelöscht
if grep -q ""$MACcable"" ""$Ausgabedatei""; then sed -i ""/.*""$MACcable"".*/d"" ""$Ausgabedatei""; fi

#Wertezeile in csv schreiben
echo ""$Werte"" >> ""$Ausgabedatei""

Und unten das ganze Skript - allerdings noch mit csv-Ausgabedatei auf dem Client:

Gruß,
Stefan

#!/bin/bash

# UNFERTIG-Skript linuxmuster.net 7.0
# _seninv
#
# zugehörige Datei:
# UNFERTIG
#
# Sammelt Systeminformationen und UNFERTIG
#
# Linux Mint 20.3 Xfce 64bit
# 2022-09-28
# GPL v3
# Sen



###Ausgabedatei festlegen (sollte später auf dem Server sein!!!)
Ausgabedatei="/tmp/info.csv"


##### Infos zum Skript

### Semikolon wird als Zellentrenner in der csv verwendet

### Folgendes ersetzt Semikolon durch Komma in den gesammelten Werten, da sonst Werte zusätzliche Zellen in der csv erzeugen könnten 
# | sed '{s/;/,/g}'

### Folgendes wandelt eine mehrzeilige Datei in einen Einzeiler um, sodass diese in der csv nur eine Zelle belegt
# readarray -t rammodules < /tmp/seninv-rammodules
# RAMmodules=$(echo ${rammodules[*]})


###Zeitstempel
Zeit=$(date +%F___%T)

###Hostname
Host=$(hostname | sed '{s/;/,/g}')

###Herstellerinfo
#Marke
Marke=$(sudo inxi -M | sed -ne '/^Machine/{s/.*System: //;s/ product.*//;p;q;}' | sed '{s/;/,/g}')
#Typ
Typ=$(sudo inxi -M | sed -ne '/^Machine/{s/.*product: //;s/ v:.*//;p;q;}' | sed '{s/;/,/g}')
#Seriennummer
Sno=$(sudo inxi -M | sed -ne '/^Machine/{s/.*serial: //;s/ .*//;p;q;}' | sed '{s/;/,/g}')

###MAC
#WiFi
MACwifi=$(iw dev | grep addr | awk '{print $2}' | sed '{s/;/,/g}')
#Cable, falls kein WiFi
if [[ "$MACwifi" == "" ]]; then  
MACcable=$(ip addr | sed -ne '/link\/ether/{s/.*ether //;s/ .*//;p;}')
#Cable, falls WiFi vorhanden
else
MACcable=$(ip addr | sed -ne '/link\/ether/{s/.*ether //;s/ .*//;p;}' | sed "/$MACwifi/d")
fi

###CPU
Cpu=$(sudo inxi -C | sed -ne '/^CPU:/{s/.*model: //;s/ bits.*//;p;q;}' | sed '{s/;/,/g}')

###RAM:
#Typ
sudo dmidecode -t memory | sed -ne '/DDR/{s/.*Type: //;s/ .*//;p;}' | uniq | sed '{s/;/,/g}' > /tmp/seninv-ramtype
readarray -t ramtype < /tmp/seninv-ramtype
RAMtype=$(echo ${ramtype[*]})
#Module
sudo dmidecode -t memory | sed -ne '/Size/{s/.*Size: //;s/ .*//;p;}' | sort | sed '{s/;/,/g}' > /tmp/seninv-rammodules
readarray -t rammodules < /tmp/seninv-rammodules
RAMmodules=$(echo ${rammodules[*]})
#RAMgesamt
RAMgesamt=$(awk '{sum+=$1}END{print sum}' /tmp/seninv-rammodules)

###Festplatte
#Größe
DISKsize=$(sudo inxi -D | sed -ne '/vendor:/{s/.*size: //;s/ .*//;p;q;}' | sed '{s/;/,/g}')
#Marke
DISKmarke=$(sudo inxi -D | sed -ne '/vendor:/{s/.*vendor: //;s/ model:.*//;p;q;}' | sed '{s/;/,/g}')
#Modell
DISKmodell=$(sudo inxi -D | sed -ne '/vendor:/{s/.*model: //;s/ size:.*//;p;q;}' | sed '{s/;/,/g}')

###Netzwerkkarte
LANkarte=$(sudo inxi -N | sed -ne '/^Network:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')

###Grafik
Grafik=$(sudo inxi -G | sed -ne '/^Graphics:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')

###Audio
Audio=$(sudo inxi -A | sed -ne '/^Audio:/{s/.*Device-1: //;s/ driver.*//;p;q;}' | sed '{s/;/,/g}')


###PCI
sudo lspci | sed '{s/;/,/g}' > /tmp/seninv-pci
readarray -t pci < /tmp/seninv-pci
PCIinfo=$(echo ${pci[*]})



###Ausgabe in csv
#Ueberschriften
Ueberschriften="Zeit;Hostname;MAC-Cable;MAC-WiFi;Marke;Typ;SerienNr;CPU;RAMtyp;RAMgesamt(MB);RAMmodule(MB);DISKsize(GB);DISKmarke;DISKmodell;LANkarte;Grafik;Audio;;PCI"
#Werte
Werte="$Zeit;$Host;$MACcable;$MACwifi;$Marke;$Typ;$Sno;$Cpu;$RAMtype;$RAMgesamt;$RAMmodules;$DISKsize;$DISKmarke;$DISKmodell;$LANkarte;$Grafik;$Audio;;$PCIinfo"

#Ausgabedatei mit Ueberschriften anlegen falls nötig
if ! [ -f ""$Ausgabedatei"" ]; then echo ""$Ueberschriften"" > ""$Ausgabedatei""; fi

#Falls MAC-Adresse in csv-Datei schon vorhanden,wird diese Wertezeile gelöscht
if grep -q ""$MACcable"" ""$Ausgabedatei""; then sed -i ""/.*""$MACcable"".*/d"" ""$Ausgabedatei""; fi

#Wertezeile in csv schreiben
echo ""$Werte"" >> ""$Ausgabedatei""



###Aufräumen
rm -f /tmp/seninv*

Hallo Michael,

Danke für den Hinweis auf hwinfo. Es läuft wohl nur unter Windows und DOS.
Da ich nur Linuxclients habe, verfolge ich das nicht weiter.

Gruß,
Stefan

Hallo Stefan,

gibt es auch für Linux! Paket: hwinfo

Beste Grüße

Thorsten

Hallo Thorsten,

Danke! Habe hwinfo jetzt ausprobiert. Leider habe ich kein csv-Export entdecken können.
Man könnte es natürlich als Grundlage nehmen, aus der man die Werte extrahiert. Dafür muss man aber die Textausgabe selbst umformatieren z.B. mit sed und grep usw.
Das wäre mir jetzt zuviel Arbeit, da ich das ja auch schon ohne hwinfo geskriptet habe.

Gruß,
Sefan

Hallo Sascha,

können wir die Lösungsansätze kombinieren, oder verfolgst Du lieber Deinen eigenen Ansatz mit python?

Gruß,
Stefan

Hi Stefan,
ich weiß noch nicht, nach dem flash gestern hatte ich noch keine Muße weiterzumachen…bei mir ist der Punkt, dass ich einen Windows-as-well Ansatz bräuchte, weil wir viele Maschinen haben, die nur unter Windows benutzt werden. Das heisst aber auch, dass ich mich bei Gelegenheit an eine Windows-Maschine setzen muss (mein eigener Haushalt ist quasi Windowsfrei) und das kostet immer etwas Überwindung…
Da aber Windows inzwischen auch ssh hat, ist das wahrscheinlich durchaus mit der einfachste Weg.

Gruß
Sascha

Hallo Sascha,

mach es doch einfach in Linbo, entweder per Postsync, oder Du weckst die Rechner mit linbo-remote auf und führst die Befehle per SSH aus. Anschließend sammelst Du den Output mit SCP ein.

Beste Grüße

Jörg

Hi Jörg,
das mit dem postsync muss ich mal ausprobieren, allerdings weiß ich nicht genau, wie ich vom postsync aus Daten zurück zum Server kriege…es sei denn natürlich, die landen eh in einem Logfile…muss ich mir mal anschauen

Gruß
Sascha

Hallo Sascha,

das müsste in etwa so gehen (mit dem Passwort des Linbo-Users):

export RSYNC_PASSWORD=„geheimespasswort“
rsync dateiname linbo@10.1.1.1::linbo-upload/subdir/dateiname

Beste Grüße

Jörg

Frage zu LINBO:

Können die CLients, die per WLAN sich verbinden denn dann auch ihre Hardwaredaten an den Server schicken?

Ich vermute nicht. Dann würden die Laptops nur beim kabelgebundenen Updaten ihre Daten senden.

Gruß,
Stefan

Hallo,

ich verwende den universellen postsync um die Datei
/srv/linbo/linuxmuster-client/ubuntu2004/common/root/.ssh/authorized_keys ,
in die ich vorher den pub key vom server (Nutzer root) reingeschrieben
habe, auf die Clients zu verteilen.
Vorsicht: die Rechte der Datei sind auf dem Server 644, damit linbo die
Datei anfassen kann.
Sie müssen aber auf dem Client 600 sein.
Also steht im postsync noch eine Zeile, die chmod 600 macht:

chmod 700 /mnt/root/.ssh/
chmod 600 /mnt/root/.ssh/authorized_keys

das funktioniert seit Jahren so.

LG

Holger

Salut Stefan,
ich habe mir jetzt eine Lösung gebastelt. die die Daten direkt auf dem Server einfach aus den Linbo-Logs liest. Ich brauche allerdings auch nicht so viele Angaben wie Du in Deinem Ansatz vorgelegt hast.

#!/bin/bash


cd /var/log/linuxmuster/linbo/
echo "Name;CPU;RAM;Platte;MAC1;MAC2"

for n in $(ls *_linbo.log)
do
  NAME=$(echo $n|cut -d_ -f1)
  CPU=$(grep -m1 ' CPU0' $n |sed 's/.* CPU0:\(.*\)[(]family.*/\1/')
  MEMORYK=$(egrep -m1 '^Memory' $n |sed 's/Memory: [0-9]*K\/\([0-9]*\)K.*/\1/')  
  HDDSIZE=$(egrep -m1 'sda.*GB' $n | sed 's/.*[(]\([0-9]* GB\).*/\1/')
  ETH0=$(egrep -m1 'eth0.*[0-9a-f]{2}:' $n | perl -pe 's/.*([0-9a-f:]{17}).*/\1/')
  ETH1=$(egrep -m1 'eth1.*[0-9a-f]{2}:' $n | perl -pe 's/.*([0-9a-f:]{17}).*/\1/')
  if [[ ! -z $CPU ]]; then
	  echo "$NAME;$CPU;$MEMORYK;$HDDSIZE;$ETH0;$ETH1"
  fi
done

Gruß
Sascha

1 „Gefällt mir“