-
Notifications
You must be signed in to change notification settings - Fork 7
Update views.py #4
base: master
Are you sure you want to change the base?
Conversation
Hello, thank you to suggest improvements. I suggest you to make two different pull request, so any of the change can be analyzed independently. |
In addition to my previous comment, I would add that there are more "native methods" in Python to compare software versions. I think we should turn the implementation in this direction because for now the version comparison between a 1.0 and 1.0.1 does not work (it stops at the second number from my reading of the code). Here is a simple code example:
|
Bonjour Damien, Je préfère répondre en français... Désolé s'il y a des anglophones intéressés ! Je ne suis pas expert en Python et j'ai tenu à conserver l'esprit du code original.
Mais la suggestion d'utiliser la classe ComparableVersion me semble intéressante. Alain |
Je poursuis avec le traitement des conditions de type lower (Logiciel non installé ou version inférieure à :) ou higher (Logiciel installé et version supérieure à :). Prenons un cas concret avec une machine sur laquelle sont installés les logiciels suivants :
Ces 2 types de conditions ont un fonctionnement qui me semble incohérent avec les autres ! J'y ai ajouté quelques améliorations :
Je suppose que le code pourrait encore être optimisé. Par exemple en remplaçant les lignes suivantes : if len(condversiontab) > len(vtab): looplimit = len(vtab) else: looplimit = len(condversiontab) par une seule instruction du genre : Cette correction évite bien des cas de bouclage ! Alain |
On peut continuer en français, ça ne me pose pas de problème ! Attention, la classe ComparableVersion dans le code proposé est un alias pour la classe LooseVersion (j'ai ajouté l'abstraction pour pouvoir changer de classe sans modifier le code qui suit). La dernière implémentation Python en date concernant les numéros de version est la suivante : NormalizedVersion. LooseVersion semble être la version la plus permissive et applicable en l'état, il existe également l'implémentation StrictVersion qui semble totalement inapplicable dans le cas de ce logiciel. Le comportement de LooseVersion avec X2go ne me semble pas poser de problèmes particuliers d'après ce que je peux en voir. Il faudrait que tu fasse des tests à l'aide du script que j'ai fourni en utilisant des numéros de versions de l'application pour confirmer ça. La limite de LooseVersion c'est la non prise en charge des versions "beta", "dev" ou autres qui seront certainement priorisés aux versions de release (sans notation spécifique). Hormis cela, je pense que ça couvre la majorité des cas. Concernant ton second message ... Ton analyse avec Java (2 versions installées) est correcte. Le fait que la version supérieure ou égale soit installée devrait empêcher le déclenchement de l'installation. Cependant la plupart de utilisateurs qui (comme moi) n'utilisent qu'une seule version ne rencontrent pas ce problème ... Pour la gestion des applications en 32 / 64 bits, je gère ça de plusieurs façons en fonction de l'application :
J'ai besoin de monter un environnement de développement côté serveur pour aller plus loin dans la réflexion et les tests (ça commence à me filer mal à la tête) je reviens vers toi prochainement. |
Bonjour, Merci, c'est très intéressant. J'apprends plein de chose sur Python ! Il y aurait effectivement une simplification non négligeable du code en utilisant cette comparaison de version. J'ai trouvé sur la page http://www.programcreek.com/python/example/6084/distutils.version.LooseVersion des exemples de comparaison de versions dont : def compare_versions(version1, version2): try: return cmp(StrictVersion(version1), StrictVersion(version2)) # in case of abnormal version number, fall back to LooseVersion except ValueError: pass try: return cmp(LooseVersion(version1), LooseVersion(version2)) except TypeError: # certain LooseVersion comparions raise due to unorderable types, # fallback to string comparison return cmp([str(v) for v in LooseVersion(version1).version], [str(v) for v in LooseVersion(version2).version]) Le code pour views.py pourrait alors être : # Software not installed or version lower than (one wildcard can be used for condition name) if install == True: for condition in pack.conditions.filter(depends='lower'): if '*' in condition.softwarename: nametab = condition.softwarename.split('*') if software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]).exists(): softtab = software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]) if compare_versions(softtab, condition.softwareversion) < 0: install = False else: if software.objects.filter(host_id=m.id, name=condition.softwarename).exists(): softtab = software.objects.filter(host_id=m.id, name=condition.softwarename) if compare_versions(softtab, condition.softwareversion) < 0: install = False if install == False: status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') # Software installed and version higher than if install == True: for condition in pack.conditions.filter(depends='higher'): if '*' in condition.softwarename: nametab = condition.softwarename.split('*') if software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]).exists(): softtab = software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]) if compare_versions(softtab, condition.softwareversion) > 0: install = False else: install = False else: if software.objects.filter(host_id=m.id, name=condition.softwarename).exists(): softtab = software.objects.filter(host_id=m.id, name=condition.softwarename) if compare_versions(softtab, condition.softwareversion) > 0: install = False else: install = False if install == False: status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') Je testes ça dès que possible ! |
Une condition avec version undefined signifie bien que la version installée n'est pas définie (champ vide). En revanche, je ne savais pas ou j'avais oublié qu'il était possible d'avoir une condition avec champ vide pour autoriser n'importe quel numéro de version (ça doit être géré au niveau des filtres). Outre Java (désormais plus problématique car j'ai un script pour désinstaller toute version de Java 8 avant l'installation d'une nouvelle version), j'ai rencontré le problème d'installations multiples avec "7-Zip *" et "VLC media player". Soit à cause de l'installation de versions 32-bits et 64-bits (nous partons d'un parc très hétérogène), soit parce que l'installation de la nouvelle version n'a pas supprimé ou remplacé l'ancienne (si je me souviens bien, c'est le cas de 7-Zip 15.x qui laisse la version 9.20). Toujours est-il qu'avec ce problème de test, un accident est vite arrivé. Si plusieurs centaines de machines installent un paquet en boucle, c'est pas terrible pour le serveur et le réseau ! De plus, le code existant n'était pas optimum : si dans un cas bien particulier vous aviez plusieurs conditions de type higher ou lower, il suffisait qu'une seule soit vrai pour que l'installation se fasse (première boucle for condition...) ! |
La fonction "compare_versions" que tu as postée, me semble être une implémentation très intéressante, je vais également faire quelques tests (en particulier sur les types d'erreurs gérées dans les except) ! Ok avec ton 2nd message, j'ai fais les mêmes constatations de mon côté. |
See : yvguim#4 - change version compare implementation by using standard Python class (StrictVersion + LooseVersion + String compare) - use OR logical operator if multiple software is matching condition
Je suis parti de ton travail, voici le résultat : https://github.com/dam09fr/updatengine-server/tree/dam09fr-patch-versioncompare |
Attention, ce n'est pas encore tout bon ! def compare_versions(version1, version2): from distutils.version import StrictVersion, LooseVersion v1 = version1.encode('ascii', 'ignore') v2 = version2.encode('ascii', 'ignore') try: return cmp(StrictVersion(v1), StrictVersion(v2)) except ValueError: # in case of abnormal version number, fall back to LooseVersion try: return cmp(LooseVersion(v1), LooseVersion(v2)) except TypeError: # certain LooseVersion comparions raise due to unorderable types, fallback to string comparison return cmp([str(v) for v in LooseVersion(v1).version], [str(v) for v in LooseVersion(v2).version]) Il est nécessaire de convertir les chaînes Unicodes en string. Pour le reste, il ne faut pas utiliser softtab, mais les numéros de versions contenus dans l'enregistrement (boucle for). De plus, c'est l'inverse au niveau des tests :
# Software not installed or version lower than (one wildcard can be used for condition name) if install == True: for condition in pack.conditions.filter(depends='lower'): if '*' in condition.softwarename: nametab = condition.softwarename.split('*') if software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]).exists(): softtab = software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]) for s in softtab: if compare_versions(s.version, condition.softwareversion) >= 0: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') break else: if software.objects.filter(host_id=m.id, name=condition.softwarename).exists(): softtab = software.objects.filter(host_id=m.id, name=condition.softwarename) for s in softtab: if compare_versions(s.version, condition.softwareversion) >= 0: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') break # Software installed and version higher than if install == True: for condition in pack.conditions.filter(depends='higher'): if '*' in condition.softwarename: nametab = condition.softwarename.split('*') if software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]).exists(): softtab = software.objects.filter(host_id=m.id, name__startswith=nametab[0],name__endswith=nametab[1]) for s in softtab: if compare_versions(s.version, condition.softwareversion) <= 0: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') break else: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') else: if software.objects.filter(host_id=m.id, name=condition.softwarename).exists(): softtab = software.objects.filter(host_id=m.id, name=condition.softwarename) for s in softtab: if compare_versions(s.version, condition.softwareversion) <= 0: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') break else: install = False status(''+str(m.id)+''+str(pack.id)+'Warning condition: '+escape(condition.name)+'') |
En effet j'ai déposé le mauvais fichier ... |
See : yvguim#4
Voilà, j'ai pris en compte tes remarques. Qu'en pense-tu ? |
Parfait, ça semble correspondre au code qui fonctionne sur mon serveur UE ! Pour tester l'existence d'un logiciel (quelle que soit la version), je ne suis pas certain de bien m'y prendre :
Si j'ai bien compris, tu mets une condition du type "Logiciel / version est installé" avec le champ version vide !? |
En fait je teste si le logiciel n'est pas installé, du coup en laissant à vide la fonction renvoie True du moment ou le logiciel possède un numéro de version. Je voulais créer un test : "logiciel est installé et version inférieure à", je suppose qu'on peut tout aussi bien créer un test "logiciel installé" |
Je note que tu as finalement intégré la conversion ASCII ! ;-) |
Je te suis dans ta réflexion ! C'était logique, les classes ne supportaient pas l'unicode. |
J'avais demandé il y a longtemps une condition du type "Logiciel installé et version inférieure à :" pour faciliter la mise à jour de logiciels qui ne sont pas affectés à un profil. Une méthode de contournement m'a été indiquée sur le Forum. Il suffit de combiner 2 conditions :
En revanche, la condition "Logiciel installé et version inférieure à :" pourrait simplifier les choses ! Concernant le Pull Request, je supprime ma demande et tu fais une demande ou je modifie ma demande ? |
Compte tenu que tu est l'initiateur et que la présente conversation est le cheminement de notre réflexion, je t'invite à modifier ta demande pour prendre en compte ce que nous avons à proposer. Pour la condition, je vais regarder ce que je peux faire ... |
Nouveau commit effectué. Concernant les conditions, je n'ai pas bien compris ta méthode pour tester la présence ou non d'un logiciel...
|
Je viens de contrôler ton commit, tout est ok pour moi ! |
Bien qu'ayant perdu tout espoir que cette modification soit un jour prise en compte, je poste ici une modification apportée afin de gérer le cas ou une condition d'installation "Logiciel non installé ou version inférieure à" ne contienne pas de numéro de version (champ vide) et qui générait une erreur bloquant tous les déploiements.
|
Bonjour Damien,
Ainsi, un numéro de version absent est assimilé à 0 (le plus petit numéro) pour tenter une comparaison, plutôt que de supposer que c'est égal. Qu'en penses-tu ? |
Bonjour, En ce qui concerne le client, j'avais espéré que l'auteur me fournirai son script de compilation python et la source InnoSetup de l'installeur mais je n'ai jamais eu de réponse... |
Ah pour ce qui est de "undefined" dans le numéro de version, je n'ai absolument rien vu dans le code qui gère ça, la chaine de caractères doit être exploitée par le code comme étant un numéro de version si ma lecture est correcte. |
Je viens de tester ton code, j'ai les erreurs suivantes dans le log du client:
|
Bonjour, |
Lors de l'inventaire, si le numéro de version d'un logiciel est absent, son champ Version est mis à "undefined". Ainsi, côté inventaire de logiciels, il ne devrait jamais y avoir la chaine vide (paramètre version1 de la fonction compare_versions). |
Je rejoins tes conclusions et je suis d'accord avec la correction proposée. |
Modify condition checking for lower and higher condition types: