Compare commits

...

11 Commits

Author SHA1 Message Date
Jason SECULA
ccece73f92 Fix MAJ GLPI magenta link 2026-02-17 11:25:04 +01:00
Jason SECULA
e01bae2243 Correction bug filtre de recherche, ajout macOS dans les types d'appareil 2026-02-05 16:38:35 +01:00
Jason SECULA
273c4dead5 Correction bug 2 2026-02-03 13:59:06 +01:00
Jason SECULA
2b021d633a Correction bug 2026-02-02 15:44:05 +01:00
Jason SECULA
7d980a744d Modification de la recherche des ordis par API suite à la MAJ de l'API GLPI 2026-01-28 14:37:36 +01:00
Jason SECULA
1168cf62f3 Ajout du fichier requirements pour les dépendances python 2025-11-20 17:38:13 +01:00
Jason SECULA
73a94c3c50 Ajout d'une suppression du verrou si la synchro dure plus de 3h 2025-11-20 17:26:16 +01:00
Jason SECULA
8e698cfc49 Ajout du lien vers la page Airwatch de l'appareil sur la fiche GLPI 2025-10-23 17:50:03 +02:00
Jason SECULA
e25463ff89 Correction erreur de frappe pour le nom de la variable 2025-10-16 09:32:36 +02:00
Jason SECULA
064ecfad43 Modification du nom de plateforme en Windows Desktop en cas de version non énumérée 2025-10-16 09:14:42 +02:00
Jason SECULA
13bc1f46d7 Ajout d'une traduction des noms de version Windows dans le friendlyname 2025-10-15 14:10:26 +02:00
5 changed files with 134 additions and 23 deletions

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
cryptography
requests
toml

View File

@@ -2,6 +2,7 @@
import os import os
import argparse import argparse
import logging import logging
import time
from functions import getSettings from functions import getSettings
from includes.airwatchAPI import * from includes.airwatchAPI import *
from includes.GLPIAPI import * from includes.GLPIAPI import *
@@ -67,12 +68,20 @@ if(args.staginguser != None):
# ====================================== # # ====================================== #
# Vérification de la présence du verrou avant de continuer # Vérification de la présence du verrou avant de continuer
if(os.path.isfile(lockFile) and not args.force): if(os.path.isfile(lockFile) and not args.force):
# Récupération du temps de création de verrou en minutes
lockTime = (time.time() - os.path.getmtime(lockFile)) // 60
# Recréation du verrou s'il existe depuis plus de 3 heures (crash)
# sinon on quitte, une synchro est déjà en cours
if(lockTime > 180):
os.remove(lockFile)
open(lockFile, "w").close()
else:
logger.debug('Lock file exists, exiting...') logger.debug('Lock file exists, exiting...')
exit(0) exit(0)
else: else:
# Création du verrou s'il n'existe pas
open(lockFile, "w").close() open(lockFile, "w").close()
# Initialisation de l'api Airwatch # Initialisation de l'api Airwatch

View File

@@ -5,6 +5,7 @@ def getSettings(settingsPath):
settingsDefault =""" settingsDefault ="""
[AIRWATCH] [AIRWATCH]
Server = "https://airwatchServer" Server = "https://airwatchServer"
ConsoleURI = "https://airwatchConsole"
APIKey = "APIKEY" APIKey = "APIKEY"
# Méthode d'authentification (CMSURL or PASSWORD) # Méthode d'authentification (CMSURL or PASSWORD)

View File

@@ -45,8 +45,9 @@ class GLPIAPI:
# Recherche des appareils en fonction du numéro de série seulement # Recherche des appareils en fonction du numéro de série seulement
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{device.SerialNumber}$' search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{device.SerialNumber}$'
searchUri = f"{self.Server}/apirest.php/search/computer?{search_parameter}" searchUri = f"{self.Server}/apirest.php/search/Computer?{search_parameter}"
search = requests.get(searchUri, headers=self.Headers) search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200): if(search.status_code == 200):
search = search.json() search = search.json()
if(search["totalcount"] == 1): if(search["totalcount"] == 1):
@@ -57,9 +58,29 @@ class GLPIAPI:
elif(search["totalcount"] > 1): elif(search["totalcount"] > 1):
deviceID = list(search["data"].keys()) deviceID = list(search["data"].keys())
return deviceID, search["data"], search["totalcount"] return deviceID, search["data"], search["totalcount"]
else:
return None, None, 0
def GetUser(self, username=None, email=None):
if(username != None):
search_parameter = f'is_deleted=0&criteria[0][field]=1&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{username}$'
elif(email != None):
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{email}$'
searchUri = f"{self.Server}/apirest.php/search/user?{search_parameter}"
search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200):
search = search.json()
if(search["totalcount"] == 1):
userID = list(search["data"].keys())[0]
data = search["data"][userID]
return userID, data, search["totalcount"]
elif(search["totalcount"] > 1):
userID = list(search["data"].keys())
return userID, search["data"], search["totalcount"]
return None, None, 0 return None, None, 0
return None, None, None
def UpdateInventory(self, inventory): def UpdateInventory(self, inventory):
headers = { headers = {
@@ -90,11 +111,43 @@ class GLPIAPI:
uri = f"{self.Server}/apirest.php/Computer/" uri = f"{self.Server}/apirest.php/Computer/"
return requests.put(uri, headers=self.Headers, json=body) return requests.put(uri, headers=self.Headers, json=body)
def UpdateAirwatchLink(self, deviceid, airwatchlink):
uri = f"{self.Server}/apirest.php/PluginFieldsComputermdt"
searchURI = f"{self.Server}/apirest.php/PluginFieldsComputermdt?range=0-999999999"
result = requests.get(searchURI, headers=self.Headers)
if(result.status_code == 200):
result = result.json()
fieldItem = None
# searching for field item
for entry in result:
if str(entry["items_id"]) == deviceid:
fieldItem = entry
if(fieldItem == None):
body = {
"input": {
"items_id": deviceid,
"itemtype": "Computer",
"plugin_fields_containers_id": 4,
"appareilsurmagentafield": airwatchlink
}
}
return requests.post(uri, headers=self.Headers, json=body)
else:
body = {
"input": {
"id": fieldItem["id"],
"appareilsurmagentafield": airwatchlink
}
}
return requests.put(uri, headers=self.Headers, json=body)
def CreateInventoryForAirwatchDevice(self, device, deviceName, apps=None): def CreateInventoryForAirwatchDevice(self, device, deviceName, apps=None):
platforms = { platforms = {
2:"Apple iOS", 2:"Apple iOS",
5:"Android", 5:"Android",
12:"Windows Desktop" 10:"Apple macOS",
12:"Windows"
} }
if(device.PlatformId in platforms.keys()): if(device.PlatformId in platforms.keys()):
@@ -119,9 +172,27 @@ class GLPIAPI:
osArch = "Unknown" osArch = "Unknown"
softwareArch = "Unknown" softwareArch = "Unknown"
windowsOSTranslation = {
"10.0.19043":"10 21H1",
"10.0.19044":"10 21H2",
"10.0.19045":"10 22H2",
"10.0.22000":"11 21H2",
"10.0.22621":"11 22H2",
"10.0.22631":"11 23H2",
"10.0.26100":"11 24H2",
"10.0.26200":"11 25H2"
}
logDate = datetime.strptime(device.LastSeen, "%Y-%m-%dT%H:%M:%S.%f").strftime("%Y-%m-%d %H:%M:%S") logDate = datetime.strptime(device.LastSeen, "%Y-%m-%dT%H:%M:%S.%f").strftime("%Y-%m-%d %H:%M:%S")
inventory = GLPIInventory(logdate=logDate, versionclient=self.UserAgent, tag=device.Group, deviceid=f"{deviceName} - {device.SerialNumber}", itemtype="Computer") inventory = GLPIInventory(logdate=logDate, versionclient=self.UserAgent, tag=device.Group, deviceid=f"{deviceName} - {device.SerialNumber}", itemtype="Computer")
if(platformName == "Windows"):
if(device.OS in windowsOSTranslation.keys()):
inventory.SetOperatingSystem(platformName, windowsOSTranslation[str(device.OS)], osArch)
else:
platformName = "Windows Desktop"
inventory.SetOperatingSystem(platformName, device.OS, osArch)
else:
inventory.SetOperatingSystem(platformName, device.OS, osArch) inventory.SetOperatingSystem(platformName, device.OS, osArch)
inventory.SetHardware(deviceName, device.Uuid, device.TotalMemory) inventory.SetHardware(deviceName, device.Uuid, device.TotalMemory)
inventory.AddUser(device.User) inventory.AddUser(device.User)

View File

@@ -2,6 +2,7 @@
import os import os
import argparse import argparse
import logging import logging
import time
from datetime import datetime from datetime import datetime
from functions import getSettings from functions import getSettings
from includes.airwatchAPI import * from includes.airwatchAPI import *
@@ -22,7 +23,7 @@ args = parser.parse_args()
if(args.configpath != None and args.configpath != ''): if(args.configpath != None and args.configpath != ''):
settings = getSettings(args.configpath) settings = getSettings(args.configpath)
else: else:
settings = getSettings("{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf") settings = getSettings(f"{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
#=========== Configuration des logs ===========# #=========== Configuration des logs ===========#
@@ -105,11 +106,21 @@ platformFilterOut = [12]
# ====================================== # # ====================================== #
# Vérification de la présence du verrou avant de continuer # Vérification de la présence du verrou avant de continuer
if(os.path.isfile(lockFile) and not args.force): if(os.path.isfile(lockFile) and not args.force):
# Récupération du temps de création de verrou en minutes
lockTime = (time.time() - os.path.getmtime(lockFile)) // 60
# Recréation du verrou s'il existe depuis plus de 3 heures (crash)
# sinon on quitte, une synchro est déjà en cours
if(lockTime > 180):
os.remove(lockFile)
open(lockFile, "w").close()
else:
logger.debug('Lock file exists, exiting...') logger.debug('Lock file exists, exiting...')
exit(0) exit(0)
else: else:
# Création du verrou s'il n'existe pas
open(lockFile, "w").close() open(lockFile, "w").close()
logger.info("========= Synchronization started =========") logger.info("========= Synchronization started =========")
@@ -185,7 +196,7 @@ if(searchFilter != None):
if(searchFilter == 'Id'): if(searchFilter == 'Id'):
devices = [device for device in devices if getattr(device, "Id") == searchValue] devices = [device for device in devices if getattr(device, "Id") == searchValue]
else: else:
devices = [device for device in devices if getattr(device, "searchFilter") == searchValue] devices = [device for device in devices if getattr(device, searchFilter) == searchValue]
for device in devices: for device in devices:
if(device.EnrollmentStatus != 'Enrolled'): if(device.EnrollmentStatus != 'Enrolled'):
@@ -204,17 +215,30 @@ for device in devices:
loggerDouble.error(f"{count} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI (GLPI ids = {', '.join(deviceID)}), skipping this device...") loggerDouble.error(f"{count} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI (GLPI ids = {', '.join(deviceID)}), skipping this device...")
continue continue
if(count == 0): if(count == 0):
loggerMissing.error(f"Device {device.FriendlyName} (id={device.Id}) not found in GLPI, is it in the trash bin ? Skipping device...") deviceIDTrash, dataTrash, countTrash = glpiapi.GetDevice(device)
if(countTrash > 1):
loggerDouble.error(f"{countTrash} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI ids = {', '.join(deviceIDTrash)}), skipping this device...")
elif(countTrash == 1):
logger.warning(f"Device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI id={deviceIDTrash}), skipping...")
else:
loggerMissing.error(f"Device {device.FriendlyName} (Airwatch id={device.Id}) not found in GLPI.")
continue continue
inventory = glpiapi.CreateInventoryForAirwatchDevice(device, data["1"], apps) inventory = glpiapi.CreateInventoryForAirwatchDevice(device, data["1"], apps)
# Mise à jour du friendly name sur Airwatch # Mise à jour du friendly name sur Airwatch
print(device.PlatformId)
platformName = inventory.operatingsystem["name"] platformName = inventory.operatingsystem["name"]
if(device.FriendlyName != f"{data['1']} {platformName} {device.OS} - {device.User}"): osVersion = inventory.operatingsystem["version"]
newFriendlyName = f"{data['1']} {platformName} {device.OS} - {device.User}" if(device.FriendlyName != f"{data['1']} {platformName} {osVersion} - {device.User}"):
newFriendlyName = f"{data['1']} {platformName} {osVersion} - {device.User}"
logger.info(f"Updating device friendlyname to {newFriendlyName}") logger.info(f"Updating device friendlyname to {newFriendlyName}")
airwatch.SetDeviceFriendlyName(device, newFriendlyName) airwatch.SetDeviceFriendlyName(device, newFriendlyName)
# Mise à jour de l'url vers la page airwatch de l'appareil sur GLPI
airwatchlink = f"{settings['AIRWATCH']['ConsoleURI']}/AirWatch/#/AirWatch/Device/Details/Summary/{device.Id}"
if(data['76689'] != airwatchlink):
glpiapi.UpdateAirwatchLink(deviceID, airwatchlink)
# filtre des plateformes # filtre des plateformes
if(platformFilterEnabled): if(platformFilterEnabled):
if device.PlatformId in platformFilterOut: if device.PlatformId in platformFilterOut:
@@ -224,9 +248,12 @@ for device in devices:
logger.info(f"Updating {deviceID} on GLPI") logger.info(f"Updating {deviceID} on GLPI")
glpiapi.UpdateInventory(inventory.Json()) glpiapi.UpdateInventory(inventory.Json())
#if(data['70'] == None and device.User != settings["AIRWATCH"]["StagingUser"]): print(f"{data['70']} - {device.User}")
#logger.info(f"Updating user from {data['70']} to {device.User} in GLPI (id={deviceID})") if(data['70'] == None and device.User != settings["AIRWATCH"]["StagingUser"]):
#glpiapi.UpdateUser(deviceID, device.User) userID, userData, userCount = glpiapi.GetUser(device.User)
if(userCount == 1):
logger.info(f"Updating user from {data['70']} to {device.User} in GLPI (id={deviceID})")
glpiapi.UpdateUser(deviceID, userID)
if(data['5'] != device.SerialNumber): if(data['5'] != device.SerialNumber):
logger.info(f"Updating serial number from {data['5']} to {device.SerialNumber} in GLPI (id={deviceID})") logger.info(f"Updating serial number from {data['5']} to {device.SerialNumber} in GLPI (id={deviceID})")