2017-10-03 4 views
3

Utiliser python3.4 J'essaie de filtrer une base de règles de pare-feu exportée en JSON et convertie en dictionnaire. Je veux filtrer basé sur des critères définis par l'utilisateur mais je ne peux pas sembler pouvoir faire cela sans constructions difficiles à lire avec beaucoup de boucles et si des déclarations.Filtrer le dictionnaire d'une manière simple et lisible

La dict de mes entrées de pare-feu est assez grande, donc je les ai raccourci un peu.

2 exemples:

entry1 = {'action': 'accept', 
      'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}], 
      'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
     } 
entry2 = {'action': 'accept', 
      'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}], 
      'srcintf': [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'}, 
         {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'}, 
         {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
     } 

Je voulais créer une structure de données pour comparer contre les 2 exemples et créé à suivre:

filter = {'action': 'accept', 
      'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
     } 

Après quelques recherches sur la façon de comparer ces structures I a pris fin avec un code lisible. Mon problème est-il n'évalue pas vrai sur inscription2 (qui a plusieurs interfaces source):

>>> filter.items() <= entry1.items() 
True 
>>> filter.items() <= entry2.items() 
False 

des conseils sur la façon dont je devrais le faire? Utilisation de la réponse Eric Duminil ci-dessous Je suis capable de créer quelque chose (voir ci-dessous), même si ce n'est toujours pas aussi lisible que je le veux. D'autres conseils?

example = entry2 
# Compare entry to filter 
noMatch = 0 
for key in filter: 
    if isinstance(example[key], list): 
     # Convert list of dicts to list for easier comparing 
     tmpExample = [d['name'] for d in example[key]] 
     # Break if entry does not contain all criteria 
     if not all(value in tmpExample for value in filter[key]): 
      noMatch = 1 
      print("No match on: " + str(filter[key])) 
      break 
    elif filter[key] != example[key]: 
     # Simple string comparing 
     noMatch = 1 
     print("No match on: " + str(filter[key])) 
     break 

if noMatch == 0: 
    print("Match") 
else: 
    print(" No match") 

Répondre

4

Théorie

La structure de données à l'intérieur 'dstintf' et 'srcintf' ne fait pas ce problème plus facile. Une liste de dicts n'est presque jamais le bon type.

En fonction de vos besoins, vous devriez probablement le convertir en un dict:

>>> data = [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'}, 
...      {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'}, 
...      {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
>>> {d['q_origin_key']:d['name'] for d in data} 
{'ZN_AUDIT': 'ZN_AUDIT', 'ZN_MGMT': 'ZN_MGMT', 'ZN_DMZ': 'ZN_DMZ'} 

Mais si les valeurs pour name et q_origin_key sont toujours égales, vous pouvez simplement utiliser une liste ou un ensemble:

>>> [d['name'] for d in data] 
['ZN_DMZ', 'ZN_MGMT', 'ZN_AUDIT'] 
>>> {d['name'] for d in data} 
set(['ZN_AUDIT', 'ZN_MGMT', 'ZN_DMZ']) 

Il devrait être plus facile de filtrer vos données maintenant, sans boucle ou if s.

Enfin, dict.items() renvoie une liste de tuples dans un ordre arbitraire. Je ne pense pas dict1.items() <= dict2.items() vous aidera pour quoi que ce soit.

Exemple

Il pourrait être une bonne idée de diviser votre code dans plusieurs fonctions et travailler avec des ensembles:

query = {'action': 'accept', 
     'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
     } 

entry1 = {'action': 'accept', 
      'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}], 
      'srcintf': [{'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
      } 

entry2 = {'action': 'accept', 
      'dstintf': [{'name': 'ZN_HDW', 'q_origin_key': 'ZN_HDW'}], 
      'srcintf': [{'name': 'ZN_DMZ', 'q_origin_key': 'ZN_DMZ'}, 
         {'name': 'ZN_MGMT', 'q_origin_key': 'ZN_MGMT'}, 
         {'name': 'ZN_AUDIT', 'q_origin_key': 'ZN_AUDIT'}] 
      } 


def compare_values(value1, value2): 
    if isinstance(value1, list) and isinstance(value2, list): 
     return set(d['name'] for d in value1).issubset(set(d['name'] for d in value2)) 
    else: 
     return value1 == value2 

def is_a_match(query, entry): 
    do_not_match = [key for key in query if not compare_values(
     query[key], entry.get(key))] 
    for key in do_not_match: 
     print("%r does not match" % key) 
    return len(do_not_match) == 0 

print(is_a_match(query, entry1)) 
# True 
print(is_a_match(query, entry2)) 
# True 
print(is_a_match({'action': 'decline'}, entry2)) 
# 'action' does not match 
# False 
+0

Merci pour l'aide à ce jour, mais mon résultat est toujours pas très lisible (voir modifier). D'autres conseils? – marvink

+0

@marvink: Voir mise à jour. Il pourrait être un peu plus lisible maintenant. –