Compare commits
27 Commits
68a0d7d6ab
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 00a4652e0b | |||
| f004382573 | |||
| d52e0b7a2a | |||
| a89a17020a | |||
| 078d4f0923 | |||
| 20f28b71db | |||
| 2bb3eec219 | |||
| 2fa704f381 | |||
| ad634c7ec3 | |||
| 84c52ae234 | |||
| 5eb7b6b778 | |||
| e0e5ea37ce | |||
| 70211dc9ac | |||
| 43c4ba4639 | |||
| b63ff9d788 | |||
| 05a0b50741 | |||
| c46e426282 | |||
| 1228e2ac2a | |||
| 0a5e780546 | |||
| ce003e206a | |||
| 3e19b4d3e6 | |||
| 074a1e5313 | |||
| f697d2fcb4 | |||
| eb1bc4cc3b | |||
| 92a6259f04 | |||
| 570b89e50c | |||
| 15a34ba66c |
108
README.md
108
README.md
@ -3,28 +3,50 @@
|
||||
## Explication de l'usage des scripts et de la configuration :
|
||||
|
||||
### Fichier de configuration global settings.json
|
||||
Les scripts prennent les informations de configuration du fichier settings.json, si celui-ci n'existe pas au lancement d'un script, il est automatiquement créé avec des valeurs d'exemples.
|
||||
Voici une liste des paramètres du fichier de configuration et les valeurs attendues (chaque valeur doit être entre " ") :
|
||||
Les scripts prennent les informations de configuration du fichier de configuration présent dans le répertoire conf, si celui-ci n'existe pas au lancement d'un script, il est automatiquement créé avec des valeurs d'exemples.
|
||||
|
||||
- **"airwatchServer"**: l'adresse du serveur Airwatch
|
||||
- **"airwatchAPIKey"**: la clé API récupéré dans Airwatch
|
||||
- **"airwatchAuthMethod"**: défini la méthode d'authentification, peut prendre la valeur ***"CMSURL"*** pour l'authentification par certificat ou ***"password"*** pour l'authentification par mot de passe.
|
||||
- **"airwatchCertPath"**: le chemin vers le certificat si la méthode d'authentification est par certificat.
|
||||
- **"airwatchCertPass"**: le mot de passe du certificat si celui-ci en possède un, laissez une chaîne vide si vous n'avez pas mis de mot de passe.
|
||||
- **"airwatchAPIUser"**: le nom de l'utilisateur pour l'authentification API dans Airwatch.
|
||||
- **"airwatchAPIPassword"**: le mot de passe de l'utilisateur si l'authentification par mot de passe est choisie.
|
||||
- **"glpiServer"**: l'adresse du serveur GLPI avec un / à la fin.
|
||||
- **"glpiAppToken"**: le token d'application GLPI.
|
||||
- **"glpiUserToken"**: le token de l'utilisateur GLPI utilisé pour les requêtes API.
|
||||
- **"stagingUser"**: le nom du compte de staging présent dans Airwatch.
|
||||
- **"userAgent"**: Un nom pour l'user agent tel qu'il sera visible dans GLPI, cela permet d'identifier l'instance Airwatch (Prod / Pré-prod) par exemple.
|
||||
Voici un exemple du fichier de configuration :
|
||||
|
||||
```toml
|
||||
[AIRWATCH]
|
||||
Server = "https://airwatchServer"
|
||||
APIKey = "APIKEY"
|
||||
# Méthode d'authentification (CMSURL or PASSWORD)
|
||||
# CMSURL permet l'authentification avec un certificat utilisateur (CertificatePath, CertificatePassword)
|
||||
# PASSWORD permet l'authentification avec un nom d'utilisateur et un mot de passe (APIUser, APIPassword)
|
||||
AuthenticationMethod = "CMSURL"
|
||||
CertificatePath = "/path/to/cert"
|
||||
CertificatePassword = "12345"
|
||||
APIUser = "UserAPI"
|
||||
APIPassword = "PasswordUserAPI"
|
||||
|
||||
# Utilisateur de staging que l'on va remplacer par l'utilisateur trouvé dans GLPI
|
||||
StagingUser = "staging-pr"
|
||||
|
||||
[GLPI]
|
||||
Server = "http://127.0.0.1/glpi"
|
||||
AppToken = "GLPIAppToken"
|
||||
UserToken = "GLPIUserToken"
|
||||
|
||||
# User agent qui sera visible sur GLPI lors de la synchronisation
|
||||
UserAgent = "Airwatch Synchronizer"
|
||||
|
||||
[LOGS]
|
||||
Enabled = true
|
||||
# Chemin où seront créé les fichiers de log
|
||||
Path = "./logs/"
|
||||
# Mode debug pour avoir plus d'informations
|
||||
Debug = false
|
||||
|
||||
```
|
||||
---
|
||||
|
||||
### syncGLPI.py
|
||||
Le script syncGLPI.py permet de synchroniser les données des appareils présents dans Airwatch avec un inventaire GLPI.
|
||||
|
||||
Au début, le script va vérifier la présence de doublons en fonction du numéro de série et garder seulement le dernier à s'être enrôlé, puis il va procéder à la vérification de la présence des appareils dans l'inventaire GLPI pour procéder à la synchronisation des données.
|
||||
Le script syncGLPI.py permet de synchroniser les données des appareils présents dans Airwatch avec un inventaire GLPI. Les actions du script :
|
||||
- vérification et suppression des doublons en fonction du numéro de série et de la date de dernier enrôlement
|
||||
- vérification de la présence des appareils dans l'inventaire GLPI et envoi d'un inventaire à partir des données d'Airwatch pour mettre à jours les informations
|
||||
- modification du numéro de série de l'appareil sur GLPI si celui-ci n'est pas identique à celui d'Airwatch
|
||||
- modification du friendlyname de l'appareil sur Airwatch à partir du nom d'inventaire de l'appareil sur GLPI
|
||||
|
||||
#### Synchronisation
|
||||
Les éléments synchronisés de Airwatch vers GLPI :
|
||||
@ -33,6 +55,7 @@ Les éléments synchronisés de Airwatch vers GLPI :
|
||||
- UUID
|
||||
- le nom du système d'exploitation et sa version
|
||||
- les logiciels présents sur la machine
|
||||
- le numéro de série
|
||||
|
||||
Les éléments synchronisés de GLPI vers Airwatch :
|
||||
- Le nom d'inventaire de la machine qui est mis pour le friendlyname
|
||||
@ -40,23 +63,31 @@ Les éléments synchronisés de GLPI vers Airwatch :
|
||||
#### Paramètres
|
||||
Ce script possède les paramètres suivants qui sont optionnels pour son exécution :
|
||||
|
||||
- **-debug** : affiche des informations lors de son exécution, utile pour résoudre des problèmes liés à des droits d'accès API ou des problèmes d'ouvertures réseaux
|
||||
- **-searchFilter** : permet de filtrer la recherche des appareils dans airwatch sur un attribut spécifique parmi la liste suivante : "Id", "SerialNumber", "Imei", "UserName"
|
||||
- **-searchValue** : la valeur pour la recherche lorsque -searchFilter est utilisé
|
||||
- **-force** : permet d'outrepasser la vérification du verrou posé par le script lors de son exécution
|
||||
- **-sF / --searchFilter** : permet de filtrer la recherche des appareils dans airwatch sur un attribut spécifique parmi la liste suivante : "Id", "SerialNumber", "Imei", "UserName"
|
||||
- **-sV / --searchValue** : la valeur pour la recherche lorsque -searchFilter est utilisé
|
||||
- **-f / --force** : permet d'outrepasser la vérification du verrou posé par le script lors de son exécution
|
||||
- **-c / --configPath** : permet de définir un chemin vers un fichier de configuration a utilisé pour l'exécution du script
|
||||
- **-s / --silent** : exécute le script sans faire de retour dans la console, les informations seront toujours présentes dans les fichiers de log
|
||||
- **-v / --verbose** : affiche des informations lors de son exécution, utile pour résoudre des problèmes liés à des droits d'accès API ou des problèmes d'ouvertures réseaux
|
||||
|
||||
---
|
||||
|
||||
### StagingUserAssignation.py
|
||||
Le script StagingUserAssignation.py permet d'assigner les appareils en staging qui sont assignés à un utilisateur de staging à l'utilisateur renseigné dans l'inventaire GLPI.
|
||||
|
||||
Il récupère le nom de l'utilisateur de staging dans le fichier settings.json.
|
||||
Il récupère le nom de l'utilisateur de staging dans le fichier de configuration.
|
||||
|
||||
#### Paramètres
|
||||
- **-debug** : affiche des informations lors de son exécution, utile pour résoudre des problèmes liés à des droits d'accès API ou des problèmes d'ouvertures réseaux
|
||||
- **-force** : permet d'outrepasser la vérification du verrou posé par le script lors de son exécution
|
||||
- **-staginguser** : permet de préciser l'utilisateur de staging pour la recherche des appareils à modifier (override le fichier de paramètres)
|
||||
- **-serialnumber** : permet de filtrer sur un numéro de série précis
|
||||
- **-u / --staginguser** : permet de préciser l'utilisateur de staging pour la recherche des appareils à modifier (override le fichier de paramètres)
|
||||
- **-sn / --serialnumber** : permet de filtrer sur un numéro de série précis
|
||||
- **-f / --force** : permet d'outrepasser la vérification du verrou posé par le script lors de son exécution
|
||||
- **-c / --configPath** : permet de définir un chemin vers un fichier de configuration a utilisé pour l'exécution du script
|
||||
- **-s / --silent** : exécute le script sans faire de retour dans la console, les informations seront toujours présentes dans les fichiers de log
|
||||
- **-v / --verbose** : affiche des informations lors de son exécution, utile pour résoudre des problèmes liés à des droits d'accès API ou des problèmes d'ouvertures réseaux
|
||||
|
||||
## Installation avec docker
|
||||
|
||||
|
||||
|
||||
## Dépendances
|
||||
Installation des paquets linux :
|
||||
@ -71,9 +102,28 @@ apt-get install -y python3 python3-pip git
|
||||
yum install -y python3 python3-pip git
|
||||
```
|
||||
|
||||
**Dépendances python**
|
||||
## Modules python nécessaires
|
||||
Voici la liste des modules python nécessaires au fonctionnement des scripts :
|
||||
- cryptography
|
||||
- requests
|
||||
- toml
|
||||
|
||||
Un fichier requirements.txt contenant les modules dont dépendent les scripts est présent dans le répertoire scripts.
|
||||
Les modules peuvent être installés de plusieurs façons :
|
||||
|
||||
**Avec le gestionnaire de paquet python Pip**
|
||||
```
|
||||
python3 -m pip install cryptography requests
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
**Avec le gestionnaire de paquet apt (debian/ubuntu)**
|
||||
```
|
||||
apt-get install -y python3-cryptography python3-requests python3-toml
|
||||
```
|
||||
|
||||
**Avec le gestionnaire de paquet yum (redhat/centos)**
|
||||
```
|
||||
yum install -y python3-cryptography python3-requests python3-toml
|
||||
```
|
||||
|
||||
## Installation des scripts
|
||||
@ -145,7 +195,7 @@ Description=Script qui assigne les appareils en staging aux utilisateurs en se b
|
||||
[Timer]
|
||||
OnUnitInactiveSec=1m
|
||||
AccuracySec=1us
|
||||
Unit=airwatchSync.service
|
||||
Unit=airwatchStaging.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
||||
28
compose.yml
Normal file
28
compose.yml
Normal file
@ -0,0 +1,28 @@
|
||||
services:
|
||||
sync:
|
||||
image: emitlinks/airwatchConnector
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./conf:/airwatchConnector/conf
|
||||
- ./certs:/airwatchConnector/certs
|
||||
- ./logs:/airwatchConnector/logs
|
||||
healthcheck:
|
||||
test: if [ -f $(ls /airwatchConnector/*_SyncGLPI.lock) ]; then exit 0; else exit 1; fi
|
||||
interval: 5m
|
||||
start_period: 3h
|
||||
environment:
|
||||
TASK: "syncGLPI"
|
||||
|
||||
stagingAssignment:
|
||||
image: emitlinks/airwatchConnector
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./conf:/airwatchConnector/conf
|
||||
- ./certs:/airwatchConnector/certs
|
||||
- ./logs:/airwatchConnector/logs
|
||||
healthcheck:
|
||||
test: if [ -f $(ls /airwatchConnector/*_StagingUserAssignation.lock) ]; then exit 0; else exit 1; fi
|
||||
interval: 15s
|
||||
start_period: 5m
|
||||
environment:
|
||||
TASK: "stagingAssignment"
|
||||
10
dockerfile
Normal file
10
dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM python
|
||||
RUN <<-EOF
|
||||
apt-get update
|
||||
apt-get install -y python3-cryptography python3-requests python3-toml
|
||||
EOF
|
||||
ADD scripts\* /airwatchConnector
|
||||
ADD pre-start.py /
|
||||
ADD start.sh /
|
||||
RUN chmod u+x /start.sh
|
||||
ENTRYPOINT ["/start.sh"]
|
||||
26
pre-start.py
Normal file
26
pre-start.py
Normal file
@ -0,0 +1,26 @@
|
||||
import os
|
||||
import toml
|
||||
|
||||
baseDir = "/airwatchConnector"
|
||||
logDir = f"{baseDir}/logs"
|
||||
confDir = f"{baseDir}/conf"
|
||||
confFiles = os.listdir(confDir)
|
||||
|
||||
# On récupère que les fichiers .conf
|
||||
confFiles = [conf for conf in confFiles if os.path.isfile(f"{confDir}/{conf}") and not conf.endswith(".conf")]
|
||||
|
||||
for conf in confFiles:
|
||||
# On forme un nom à partir du nom du fichier de conf sans l'extension
|
||||
# et on enlève les espaces
|
||||
confName = conf[:5].replace(' ', '')
|
||||
|
||||
with open(f"{confDir}/{conf}", "r") as f:
|
||||
settings = toml.load(f)
|
||||
|
||||
# Create log folder
|
||||
if(settings["LOGS"]["Enabled"]):
|
||||
logPath = settings["LOGS"].get(Path)
|
||||
if not os.path.exists(f"{logDir}/{logPath}"):
|
||||
os.makedirs(f"{logDir}/{logPath}")
|
||||
|
||||
|
||||
17
scripts/StagingUserAssignation.py
Normal file → Executable file
17
scripts/StagingUserAssignation.py
Normal file → Executable file
@ -20,7 +20,7 @@ args = parser.parse_args()
|
||||
if(args.configpath != None and args.configpath != ''):
|
||||
settings = getSettings(args.configpath)
|
||||
else:
|
||||
settings = getSettings("./conf/settings.conf")
|
||||
settings = getSettings(f"{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
|
||||
|
||||
#=========== Configuration des logs ===========#
|
||||
|
||||
@ -41,7 +41,7 @@ if(settings["LOGS"]["Enabled"]):
|
||||
if(settings["LOGS"].get("Path") and settings["LOGS"].get("Path") != ""):
|
||||
fileHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}stagingUserAssignation.log")
|
||||
else:
|
||||
fileHandler = logging.FileHandler('./logs/stagingUserAssignation.log')
|
||||
fileHandler = logging.FileHandler(f'{os.path.realpath(os.path.dirname(__file__))}/logs/stagingUserAssignation.log')
|
||||
fileHandler.setLevel(logginglevel)
|
||||
fileHandler.setFormatter(formatter)
|
||||
logger.addHandler(fileHandler)
|
||||
@ -56,7 +56,8 @@ if(not args.silent):
|
||||
#======== Paramètres du script ========#
|
||||
|
||||
# Emplacement du verrou
|
||||
lockFile = './airwatchStagingUserAssignation.lock'
|
||||
nameForLockFile = settings["GLPI"]["UserAgent"].replace(' ', '-')
|
||||
lockFile = f'{os.path.realpath(os.path.dirname(__file__))}/{nameForLockFile}_StagingUserAssignation.lock'
|
||||
|
||||
debug=args.debug
|
||||
|
||||
@ -142,9 +143,15 @@ for device in devices:
|
||||
else:
|
||||
logger.warning(f"Device with id {device.Id} is not assigned to any user in GLPI, skipping the device")
|
||||
elif(deviceCount > 1):
|
||||
logger.info(f"More than one entry found in GLPI for device with id {device.Id}")
|
||||
logger.error(f"{count} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI ids = {', '.join(deviceID)}), skipping this device...")
|
||||
else:
|
||||
logger.error(f"Device {device.Id} with serialnumber {device.SerialNumber} not found in GLPI (in trash bin ?)")
|
||||
deviceIDTrash, dataTrash, deviceCountTrash = glpiapi.GetDevice(device, trashbin=True)
|
||||
if(countTrash > 1):
|
||||
logger.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:
|
||||
logger.error(f"Device {device.FriendlyName} (Airwatch id={device.Id}) not found in GLPI.")
|
||||
|
||||
|
||||
# Suppression du verrou
|
||||
|
||||
0
scripts/certs/empty
Normal file
0
scripts/certs/empty
Normal file
@ -1,4 +1,3 @@
|
||||
|
||||
[AIRWATCH]
|
||||
Server = "https://airwatchServer"
|
||||
APIKey = "APIKEY"
|
||||
@ -19,10 +18,13 @@ StagingUser = "staging-pr"
|
||||
Server = "http://127.0.0.1/glpi"
|
||||
AppToken = "GLPIAppToken"
|
||||
UserToken = "GLPIUserToken"
|
||||
|
||||
# User agent qui sera visible sur GLPI lors de la synchronisation
|
||||
UserAgent = "Airwatch Synchronizer"
|
||||
|
||||
[LOGS]
|
||||
Enabled = true
|
||||
# Chemin où seront créé les fichiers de log
|
||||
Path = "./logs/"
|
||||
# Mode debug pour avoir plus d'informations
|
||||
Debug = false
|
||||
3
scripts/functions.py
Normal file → Executable file
3
scripts/functions.py
Normal file → Executable file
@ -23,12 +23,15 @@ StagingUser = "staging-pr"
|
||||
Server = "http://127.0.0.1/glpi"
|
||||
AppToken = "GLPIAppToken"
|
||||
UserToken = "GLPIUserToken"
|
||||
|
||||
# User agent qui sera visible sur GLPI lors de la synchronisation
|
||||
UserAgent = "Airwatch Synchronizer"
|
||||
|
||||
[LOGS]
|
||||
Enabled = true
|
||||
# Chemin où seront créé les fichiers de log
|
||||
Path = "./logs/"
|
||||
# Mode debug pour avoir plus d'informations
|
||||
Debug = false
|
||||
"""
|
||||
|
||||
|
||||
35
scripts/includes/GLPIAPI.py
Normal file → Executable file
35
scripts/includes/GLPIAPI.py
Normal file → Executable file
@ -55,17 +55,39 @@ class GLPIAPI:
|
||||
|
||||
return deviceID, data, search["totalcount"]
|
||||
elif(search["totalcount"] > 1):
|
||||
deviceID = list(search["data"].keys())
|
||||
return deviceID, search["data"], search["totalcount"]
|
||||
else:
|
||||
return None, None, 0
|
||||
return None, None, None
|
||||
|
||||
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"]
|
||||
else:
|
||||
return None, None, 0
|
||||
|
||||
def UpdateInventory(self, inventory):
|
||||
headers = {
|
||||
"Content-Type":"Application/x-compress",
|
||||
"user-agent":self.UserAgent
|
||||
}
|
||||
return requests.post(self.Server, headers=headers, json=inventory)
|
||||
return requests.post(self.Server, headers=headers, data=inventory)
|
||||
|
||||
def UpdateSerialNumber(self, deviceid, serialnumber):
|
||||
|
||||
@ -78,6 +100,17 @@ class GLPIAPI:
|
||||
uri = f"{self.Server}/apirest.php/Computer/"
|
||||
return requests.put(uri, headers=self.Headers, json=body)
|
||||
|
||||
def UpdateUser(self, deviceid, username):
|
||||
|
||||
body = {
|
||||
"input" : {
|
||||
"id" : deviceid,
|
||||
"users_id" : username
|
||||
}
|
||||
}
|
||||
uri = f"{self.Server}/apirest.php/Computer/"
|
||||
return requests.put(uri, headers=self.Headers, json=body)
|
||||
|
||||
def CreateInventoryForAirwatchDevice(self, device, deviceName, apps=None):
|
||||
platforms = {
|
||||
2:"Apple iOS",
|
||||
|
||||
0
scripts/includes/__init__.py
Normal file → Executable file
0
scripts/includes/__init__.py
Normal file → Executable file
13
scripts/includes/airwatchAPI.py
Normal file → Executable file
13
scripts/includes/airwatchAPI.py
Normal file → Executable file
@ -168,7 +168,10 @@ class AirwatchUser:
|
||||
self.Group = user["Group"]
|
||||
self.GroupId = user["LocationGroupId"]
|
||||
self.OrgUuid = user["OrganizationGroupUuid"]
|
||||
self.DeviceCount = int(user["EnrolledDevicesCount"])
|
||||
if(user["EnrolledDevicesCount"] != ''):
|
||||
self.DeviceCount = int(user["EnrolledDevicesCount"])
|
||||
else:
|
||||
self.DeviceCount = 0
|
||||
|
||||
class AirwatchDevice:
|
||||
|
||||
@ -182,8 +185,12 @@ class AirwatchDevice:
|
||||
self.GroupId = device["LocationGroupId"]["Id"]["Value"]
|
||||
self.Group = device["LocationGroupName"]
|
||||
self.GroupUuid = device["LocationGroupId"]["Uuid"]
|
||||
self.UserId = device["UserId"]["Id"]["Value"]
|
||||
self.User = device["UserName"]
|
||||
if(device["UserId"].get("Id") != None):
|
||||
self.UserId = device["UserId"]["Id"]["Value"]
|
||||
self.User = device["UserName"]
|
||||
else:
|
||||
self.UserId = None
|
||||
self.User = None
|
||||
self.UserEmail = device["UserEmailAddress"]
|
||||
self.PlatformId = device["PlatformId"]["Id"]["Value"]
|
||||
self.Platform = device["Platform"]
|
||||
|
||||
0
scripts/logs/empty
Normal file
0
scripts/logs/empty
Normal file
3
scripts/requirements.txt
Normal file
3
scripts/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
cryptography
|
||||
requests
|
||||
toml
|
||||
60
scripts/syncGLPI.py
Normal file → Executable file
60
scripts/syncGLPI.py
Normal file → Executable file
@ -22,11 +22,16 @@ args = parser.parse_args()
|
||||
if(args.configpath != None and args.configpath != ''):
|
||||
settings = getSettings(args.configpath)
|
||||
else:
|
||||
settings = getSettings("./conf/settings.conf")
|
||||
settings = getSettings("{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
|
||||
|
||||
#=========== Configuration des logs ===========#
|
||||
|
||||
# handler pour les logs de base
|
||||
logger = logging.getLogger(__name__)
|
||||
# handler pour log les doublons dans GLPI
|
||||
loggerDouble = logging.getLogger('doubleGLPI')
|
||||
# hander pour log les appareils manquants dans GLPI
|
||||
loggerMissing = logging.getLogger('missingGLPI')
|
||||
|
||||
if(args.debug or settings["LOGS"]["Debug"]):
|
||||
logginglevel = logging.DEBUG
|
||||
@ -34,18 +39,40 @@ else:
|
||||
logginglevel = logging.INFO
|
||||
|
||||
logger.setLevel(logginglevel)
|
||||
loggerDouble.setLevel(logginglevel)
|
||||
loggerMissing.setLevel(logginglevel)
|
||||
|
||||
formatter = logging.Formatter(fmt='%(asctime)s | %(levelname)s: %(message)s', datefmt='%Y/%m/%d %H:%M:%S')
|
||||
|
||||
# handler pour log dans un fichier
|
||||
if(settings["LOGS"]["Enabled"]):
|
||||
# File Handler
|
||||
if(settings["LOGS"].get("Path") and settings["LOGS"].get("Path") != ""):
|
||||
fileHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}syncGLPI.log")
|
||||
fileErrorHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}syncGLPI-errors.log")
|
||||
fileDoubleHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}syncGLPI-double.log")
|
||||
fileMissingHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}syncGLPI-missing.log")
|
||||
else:
|
||||
fileHandler = logging.FileHandler('./logs/syncGLPI.log')
|
||||
fileHandler = logging.FileHandler('{os.path.realpath(os.path.dirname(__file__))}/logs/syncGLPI.log')
|
||||
fileErrorHandler = logging.FileHandler("{os.path.realpath(os.path.dirname(__file__))}/logs/syncGLPI-errors.log")
|
||||
fileDoubleHandler = logging.FileHandler("{os.path.realpath(os.path.dirname(__file__))}/logs/syncGLPI-double.log")
|
||||
fileMissingHandler = logging.FileHandler("{os.path.realpath(os.path.dirname(__file__))}/logs/syncGLPI-missing.log")
|
||||
|
||||
# Set Logging Level to files handler
|
||||
fileHandler.setLevel(logginglevel)
|
||||
fileErrorHandler.setLevel(logging.ERROR)
|
||||
fileDoubleHandler.setLevel(logging.ERROR)
|
||||
fileMissingHandler.setLevel(logging.ERROR)
|
||||
# Set Formatter to file handler
|
||||
fileHandler.setFormatter(formatter)
|
||||
fileErrorHandler.setFormatter(formatter)
|
||||
fileDoubleHandler.setFormatter(formatter)
|
||||
fileMissingHandler.setFormatter(formatter)
|
||||
# Add Handler to loggers
|
||||
logger.addHandler(fileHandler)
|
||||
logger.addHandler(fileErrorHandler)
|
||||
loggerDouble.addHandler(fileDoubleHandler)
|
||||
loggerMissing.addHandler(fileMissingHandler)
|
||||
|
||||
# handler pour log dans la console
|
||||
if(not args.silent):
|
||||
@ -57,7 +84,8 @@ if(not args.silent):
|
||||
#======== Paramètres du script ========#
|
||||
|
||||
# Emplacement du verrou
|
||||
lockFile = './airwatchSyncGLPI.lock'
|
||||
nameForLockFile = settings["GLPI"]["UserAgent"].replace(' ', '-')
|
||||
lockFile = f'{os.path.realpath(os.path.dirname(__file__))}/{nameForLockFile}_SyncGLPI.lock'
|
||||
|
||||
logger.debug(f"============ Settings ============")
|
||||
logger.debug(f"Airwatch server: {settings['AIRWATCH']['Server']}")
|
||||
@ -161,18 +189,28 @@ if(searchFilter != None):
|
||||
|
||||
for device in devices:
|
||||
if(device.EnrollmentStatus != 'Enrolled'):
|
||||
logger.warning(f"Device with id {device.Id} not enrolled, skipping this device...")
|
||||
logger.warning(f"Device with Airwatch id {device.Id} not enrolled, skipping this device...")
|
||||
continue
|
||||
|
||||
logger.info(f"Searching device {device.FriendlyName} (id={device.Id}) on GLPI")
|
||||
if(device.SerialNumber == 'HUBNOSERIAL'):
|
||||
logger.info(f"Device with Airwatch id {device.Id} is using work profile, skipping...")
|
||||
continue
|
||||
|
||||
logger.info(f"Searching device {device.FriendlyName} (Airwatch id={device.Id}) on GLPI")
|
||||
|
||||
deviceID, data, count = glpiapi.GetDevice(device)
|
||||
apps = airwatch.GetDeviceApps(device)
|
||||
if(count > 1):
|
||||
logger.error(f"{count} devices matching airwatch device 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
|
||||
if(count == 0):
|
||||
logger.error(f"Device 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
|
||||
|
||||
inventory = glpiapi.CreateInventoryForAirwatchDevice(device, data["1"], apps)
|
||||
@ -192,8 +230,14 @@ for device in devices:
|
||||
logger.info(f"Updating {deviceID} on GLPI")
|
||||
glpiapi.UpdateInventory(inventory.Json())
|
||||
|
||||
if(data['70'] == None and device.User != settings["AIRWATCH"]["StagingUser"]):
|
||||
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):
|
||||
logger.info(f"Updating serial number from {data['5']} to {device.SerialNumber} in GLPI")
|
||||
logger.info(f"Updating serial number from {data['5']} to {device.SerialNumber} in GLPI (id={deviceID})")
|
||||
glpiapi.UpdateSerialNumber(deviceID, device.SerialNumber)
|
||||
|
||||
logger.info("========= End of synchronization =========")
|
||||
|
||||
39
start.sh
Normal file
39
start.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
python /pre-start.py
|
||||
|
||||
if [ $CONF != '' ]; then
|
||||
case $TASK in
|
||||
|
||||
syncGLPI)
|
||||
python3 /airwatchConnector/sync.py -c $CONF -f
|
||||
;;
|
||||
|
||||
stagingAssignment)
|
||||
python3 /airwatchConnector/stagingUserAssignation.py -c $CONF -f
|
||||
;;
|
||||
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
else
|
||||
confFiles = $(ls /airwatchConnector/conf/*.conf)
|
||||
if [ $confFiles = '' ]; then exit 1; do
|
||||
for CONF in $confFiles; do
|
||||
case $TASK in
|
||||
|
||||
syncGLPI)
|
||||
python3 /airwatchConnector/sync.py -c $CONF -for
|
||||
;;
|
||||
|
||||
stagingAssignment)
|
||||
python3 /airwatchConnector/stagingUserAssignation.py -c $CONF -f
|
||||
;;
|
||||
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
Reference in New Issue
Block a user