Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00a4652e0b | |||
| f004382573 | |||
| d52e0b7a2a | |||
| ad634c7ec3 | |||
| 84c52ae234 | |||
| 5eb7b6b778 | |||
| e0e5ea37ce | |||
| 70211dc9ac | |||
| 43c4ba4639 | |||
| b63ff9d788 | |||
| 05a0b50741 | |||
| c46e426282 | |||
| 1228e2ac2a |
31
README.md
31
README.md
@ -3,7 +3,7 @@
|
|||||||
## Explication de l'usage des scripts et de la configuration :
|
## Explication de l'usage des scripts et de la configuration :
|
||||||
|
|
||||||
### Fichier de configuration global settings.json
|
### Fichier de configuration global settings.json
|
||||||
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. Un chemin personnalisé vers un fichier de configuration peut être renseigné avec le paramètre -c.
|
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.
|
||||||
|
|
||||||
Voici un exemple du fichier de configuration :
|
Voici un exemple du fichier de configuration :
|
||||||
|
|
||||||
@ -85,6 +85,10 @@ Il récupère le nom de l'utilisateur de staging dans le fichier de configuratio
|
|||||||
- **-s / --silent** : exécute le script sans faire de retour dans la console, les informations seront toujours présentes dans les fichiers de log
|
- **-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
|
- **-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
|
## Dépendances
|
||||||
Installation des paquets linux :
|
Installation des paquets linux :
|
||||||
|
|
||||||
@ -98,9 +102,28 @@ apt-get install -y python3 python3-pip git
|
|||||||
yum 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 toml
|
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
|
## Installation des scripts
|
||||||
@ -172,7 +195,7 @@ Description=Script qui assigne les appareils en staging aux utilisateurs en se b
|
|||||||
[Timer]
|
[Timer]
|
||||||
OnUnitInactiveSec=1m
|
OnUnitInactiveSec=1m
|
||||||
AccuracySec=1us
|
AccuracySec=1us
|
||||||
Unit=airwatchSync.service
|
Unit=airwatchStaging.service
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=timers.target
|
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 != ''):
|
if(args.configpath != None and args.configpath != ''):
|
||||||
settings = getSettings(args.configpath)
|
settings = getSettings(args.configpath)
|
||||||
else:
|
else:
|
||||||
settings = getSettings("./conf/settings.conf")
|
settings = getSettings(f"{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
|
||||||
|
|
||||||
#=========== Configuration des logs ===========#
|
#=========== Configuration des logs ===========#
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ if(settings["LOGS"]["Enabled"]):
|
|||||||
if(settings["LOGS"].get("Path") and settings["LOGS"].get("Path") != ""):
|
if(settings["LOGS"].get("Path") and settings["LOGS"].get("Path") != ""):
|
||||||
fileHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}stagingUserAssignation.log")
|
fileHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}stagingUserAssignation.log")
|
||||||
else:
|
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.setLevel(logginglevel)
|
||||||
fileHandler.setFormatter(formatter)
|
fileHandler.setFormatter(formatter)
|
||||||
logger.addHandler(fileHandler)
|
logger.addHandler(fileHandler)
|
||||||
@ -56,7 +56,8 @@ if(not args.silent):
|
|||||||
#======== Paramètres du script ========#
|
#======== Paramètres du script ========#
|
||||||
|
|
||||||
# Emplacement du verrou
|
# 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
|
debug=args.debug
|
||||||
|
|
||||||
@ -142,9 +143,15 @@ for device in devices:
|
|||||||
else:
|
else:
|
||||||
logger.warning(f"Device with id {device.Id} is not assigned to any user in GLPI, skipping the device")
|
logger.warning(f"Device with id {device.Id} is not assigned to any user in GLPI, skipping the device")
|
||||||
elif(deviceCount > 1):
|
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:
|
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
|
# Suppression du verrou
|
||||||
|
|||||||
0
scripts/functions.py
Normal file → Executable file
0
scripts/functions.py
Normal file → Executable file
21
scripts/includes/GLPIAPI.py
Normal file → Executable file
21
scripts/includes/GLPIAPI.py
Normal file → Executable file
@ -61,6 +61,27 @@ class GLPIAPI:
|
|||||||
return None, None, 0
|
return None, None, 0
|
||||||
return None, None, None
|
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):
|
def UpdateInventory(self, inventory):
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type":"Application/x-compress",
|
"Content-Type":"Application/x-compress",
|
||||||
|
|||||||
0
scripts/includes/__init__.py
Normal file → Executable file
0
scripts/includes/__init__.py
Normal file → Executable file
0
scripts/includes/airwatchAPI.py
Normal file → Executable file
0
scripts/includes/airwatchAPI.py
Normal file → Executable file
3
scripts/requirements.txt
Normal file
3
scripts/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
cryptography
|
||||||
|
requests
|
||||||
|
toml
|
||||||
16
scripts/syncGLPI.py
Normal file → Executable file
16
scripts/syncGLPI.py
Normal file → Executable file
@ -204,7 +204,13 @@ 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)
|
||||||
@ -224,9 +230,11 @@ 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"]):
|
if(data['70'] == None and device.User != settings["AIRWATCH"]["StagingUser"]):
|
||||||
#logger.info(f"Updating user from {data['70']} to {device.User} in GLPI (id={deviceID})")
|
userID, userData, userCount = glpiapi.GetUser(device.User)
|
||||||
#glpiapi.UpdateUser(deviceID, 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})")
|
||||||
|
|||||||
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