[PYTHON] Fusionner les données au format JSON avec Ansible

introduction

Dans Article précédent, comparez les données JSON prédéfinies avec les données JSON définies pour voir si les paramètres sont reflétés comme prévu. fait. Cette fois, je voudrais fusionner deux données JSON à l'aide d'Ansible. Plus précisément, avant de configurer les paramètres d'interface Cisco IOS-XE dans RESTCONF, essayez de fusionner les paramètres existants avec des paramètres supplémentaires.

1. Environnement préparé

J'ai utilisé CSR1000v 16.9.3 de IOS XE sur le code recommandé CSR de Cisco DevNet Sandbox. C'était. J'ai installé et utilisé Ansible 2.9.2 sur l'environnement virtuel venv de Python 3.6.8.

2. Plug-in de filtre

Importé JSONMerge en tant que plug-in de filtre Ansible. Il s'agit d'un module basé sur Python qui fusionne la tête (données JSON supplémentaires) avec la base (données JSON d'origine) et génère le résultat. Vous pouvez spécifier la méthode de fusion dans les stratégies de fusion. S'il n'est pas spécifié, ce sera «écraser» et l'ancienne valeur sera écrasée. En fait, je pense qu'il y a de nombreux cas où vous voulez ajouter des éléments aux données du tableau [〇, △]. Dans ce cas, vous pouvez sélectionner ʻappend (simplement ajouter) ou ʻarrayMergeById (fusionner si la valeur de clé spécifique dans l'élément est la même, en ajouter une nouvelle). Un exemple sera présenté plus tard, mais vous pouvez spécifier les données à traiter et comment les traiter de la même manière que JSON Schema.

2-1. Préparation préalable

Installez JSON Merge.

$ pip install jsonmerge

2-2. Code Python (extrait)

Semblable à cet article, les fichiers suivants sont stockés dans le répertoire filter_plugins. (En fait, d'autres filtres personnalisés sont également écrits dans le même fichier.) Ce que nous faisons, ce sont les deux points suivants.

custom_filters1.py


from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ansible.module_utils.six import PY3, string_types
from ansible.errors import AnsibleError, AnsibleFilterError

try:
    from jsonmerge import Merger
    HAS_JSONMERGE = True
except ImportError:
    HAS_JSONMERGE = False


class FilterModule(object):

    def jsonmerge(self, output1, output2, schema):
        if not HAS_JSONMERGE:
            raise AnsibleFilterError("JSONMerge not found. Run 'pip install jsonmerge'")
        merger = Merger(schema)
        merge_result = merger.merge(output1, output2)
        return merge_result

    def filters(self):
        return {
            'jsonmerge': self.jsonmerge
        }

3. Fichier d'inventaire

Identique à Article précédent J'ai utilisé quelque chose.

  1. Playbook Le contenu de chaque tâche est le suivant.

  2. Obtenez les paramètres d'interface existants avec le module restconf_get (1 de GigabieEthernet1 à 3 a déjà été défini)

  3. Sortie du résultat de la tâche 1

  4. Définissez les paramètres de description, de déblocage (pas d'arrêt) et d'adresse IP GigabitEthernet2 dans la variable de lecture content_data. Fusionnez le résultat de la tâche 1 avec content_data avec le plug-in de filtre jsonmerge. À ce stade, dans la variable de tâche schema, spécifiez la stratégie de fusion du paramètre d'interface (ʻinterface:) à ʻarrayMergeById. Spécifiez que le nom de l'interface est l'ID (ʻidRef: name`) et le même ID fusionne les paramètres.

playbook_restconf_int_merge1.yml


---

- hosts: cisco
  gather_facts: no

  vars:
    content_data:
      ietf-interfaces:interfaces:
        interface:
          - description: "For TEST"
            enabled: true
            ietf-ip:ipv4:
              address:
                - ip: 192.168.1.1
                  netmask: 255.255.255.0
            name: GigabitEthernet2

  tasks:
    - name: get interface info   # (1)
      restconf_get:
        path: /data/ietf-interfaces:interfaces
      register: result_before

    - name: display output data   # (2)
      debug:
        msg: "{{ result_before.response }}"

    - name: merge existing and setup interface settings   # (3)
      debug:
        msg: "{{ result_before.response | jsonmerge(content_data, schema) }}"
      vars:
        schema:
          properties:
            ietf-interfaces:interfaces:
              properties:
                interface:
                  mergeStrategy: arrayMergeById
                  mergeOptions:
                    idRef: name

5. Résultat de sortie

Si vous regardez le résultat de la tâche 3, vous pouvez voir qu'ils sont fusionnés sans aucun problème.

$ ansible-playbook -i inventory_restconf1.ini playbook_restconf_int_merge1.yml

PLAY [cisco] **********************************************************************************************************

TASK [get interface info] *********************************************************************************************
ok: [csr1000v-1]

TASK [display output data] ********************************************************************************************
ok: [csr1000v-1] => {
    "msg": {
        "ietf-interfaces:interfaces": {
            "interface": [
                {
                    "description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
                    "enabled": true,
                    "ietf-ip:ipv4": {
                        "address": [
                            {
                                "ip": "10.10.20.48",
                                "netmask": "255.255.255.0"
                            }
                        ]
                    },
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet1",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "enabled": false,
                    "ietf-ip:ipv4": {},
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet2",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "description": "Network Interface",
                    "enabled": false,
                    "ietf-ip:ipv4": {},
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet3",
                    "type": "iana-if-type:ethernetCsmacd"
                }
            ]
        }
    }
}

TASK [merge existing and setup interface settings] ********************************************************************
ok: [csr1000v-1] => {
    "msg": {
        "ietf-interfaces:interfaces": {
            "interface": [
                {
                    "description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
                    "enabled": true,
                    "ietf-ip:ipv4": {
                        "address": [
                            {
                                "ip": "10.10.20.48",
                                "netmask": "255.255.255.0"
                            }
                        ]
                    },
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet1",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "description": "For TEST",
                    "enabled": true,
                    "ietf-ip:ipv4": {
                        "address": [
                            {
                                "ip": "192.168.1.1",
                                "netmask": "255.255.255.0"
                            }
                        ]
                    },
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet2",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "description": "Network Interface",
                    "enabled": false,
                    "ietf-ip:ipv4": {},
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet3",
                    "type": "iana-if-type:ethernetCsmacd"
                }
            ]
        }
    }
}

PLAY RECAP ************************************************************************************************************
csr1000v-1                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

(Référence) Modifier les paramètres de l'interface

Pour votre information, collez le résultat des paramètres d'interface réels dans le module ʻuri`.

(Référence 1) Playbook pour modifier les paramètres

playbook_restconf_int_merge2.yml


---

- hosts: cisco
  gather_facts: no

  vars:
    content_data:
      ietf-interfaces:interfaces:
        interface:
          - description: "For TEST"
            enabled: true
            ietf-ip:ipv4:
              address:
                - ip: 192.168.1.1
                  netmask: 255.255.255.0
            name: GigabitEthernet2

  tasks:
    - name: merge interface settings
      uri:
        url: https://{{ansible_host}}:{{ansible_httpapi_port}}/restconf/data/ietf-interfaces:interfaces
        method: PATCH
        headers:
          Content-Type: application/yang-data+json
          Accept: application/yang-data+json
        body_format: json
        body: "{{ content_data | to_json }}"
        status_code:
          - 200
          - 204
        url_username: "{{ ansible_user }}"
        url_password: "{{ ansible_password }}"
        force_basic_auth: yes
        validate_certs: no

    - name: get interface info
      restconf_get:
        path: /data/ietf-interfaces:interfaces
      register: result

    - name: display output data
      debug:
        msg: "{{ result.response }}"

(Référence 2) Résultat de sortie du changement de réglage

$ ansible-playbook -i inventory_restconf1.ini playbook_restconf_int_merge2.yml

PLAY [cisco] **********************************************************************************************************

TASK [merge interface settings] ***************************************************************************************
ok: [csr1000v-1]

TASK [get interface info] *********************************************************************************************
ok: [csr1000v-1]

TASK [display output data] ********************************************************************************************
ok: [csr1000v-1] => {
    "msg": {
        "ietf-interfaces:interfaces": {
            "interface": [
                {
                    "description": "MANAGEMENT INTERFACE - DON'T TOUCH ME",
                    "enabled": true,
                    "ietf-ip:ipv4": {
                        "address": [
                            {
                                "ip": "10.10.20.48",
                                "netmask": "255.255.255.0"
                            }
                        ]
                    },
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet1",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "description": "For TEST",
                    "enabled": true,
                    "ietf-ip:ipv4": {
                        "address": [
                            {
                                "ip": "192.168.1.1",
                                "netmask": "255.255.255.0"
                            }
                        ]
                    },
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet2",
                    "type": "iana-if-type:ethernetCsmacd"
                },
                {
                    "description": "Network Interface",
                    "enabled": false,
                    "ietf-ip:ipv4": {},
                    "ietf-ip:ipv6": {},
                    "name": "GigabitEthernet3",
                    "type": "iana-if-type:ethernetCsmacd"
                }
            ]
        }
    }
}

PLAY RECAP ************************************************************************************************************
csr1000v-1                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

finalement

Dans ce cas, le résultat de fusion attendu a été obtenu. Cependant, si je voulais ajouter plusieurs données de tableau imbriquées au lieu de l'écraser, le schéma JSON était en conflit et ne fonctionnait pas. Il y a peut-être un moyen de le faire, mais j'ai atteint la limite de mon énergie et j'aimerais réessayer: transpirer:

Recommended Posts

Fusionner les données au format JSON avec Ansible
Formater json avec Vim (avec python)
Lire des données json avec python
Exporter les données DB au format json
[Introduction à Python] Comment gérer les données au format JSON
Convertir les données au format json en txt (en utilisant yolo)
Mémo de flux lors de la récupération de données json avec urllib
Analyse de données avec python 2
Utiliser ansible avec cygwin
[Python] Utiliser JSON avec Python
Fusionner les ensembles de données avec les pandas
Lecture de données avec TensorFlow
analyse json avec gdb
Journal de sortie au format JSON avec journalisation standard Python
Visualisation des données avec les pandas
Lire le fichier json avec Python, le formater et générer le json
Mélangez les données avec les pandas
Augmentation des données avec openCV
Normariser les données avec Scipy
Format SQL avec sqlparse
CHARGER DES DONNÉES avec PyMysql
[Python] Utiliser les données au format JSON comme objet de type dictionnaire
Exemple de données créées avec python
Incorporer des données audio avec Jupyter
Graphique des données Excel avec matplotlib (1)
Charger json imbriqué avec des pandas
POST json avec le script Python 3
Traitement des données 3 (développement) À propos du format des données
Extraire les données Twitter avec CSV
Obtenez des données Youtube avec python
Écrire des données au format HDF
Binariser les données photo avec OpenCV
Graphique des données Excel avec matplotlib (2)
Utilisez Django pour enregistrer les données de tweet
Formater la source du langage C avec pycparser
Travailler avec des fichiers JSON dans Matlab
Format de chaîne avec l'opérateur Python%
Conseils de traitement des données avec Pandas
Obtenez des données sur le maquereau et crachez du json pour un inventaire dynamique ansible