Search code examples
ansiblegraphqlcics

Can I navigate relationships between CICS resouces from Ansible?


I've been getting started with the CICS collection on Ansible Galaxy. I've been using it to do things like request individual resources, but I can't see a way of traversing relationships like you would do in CICS' CMCI GraphQL API.

Does the collection have that ability?


Solution

  • No, the CICS collection doesn't have the ability to use the CMCI GraphQL API. It purely uses the CMCI REST API which just deals with one resource type at a type.

    However, that doesn't mean you can't use the CMCI GraphQL API to CICS from Ansible! That API is generally a bit more understandable and you can build up queries using GraphiQL without needing a special collection. Then, you can use Ansible's built-in uri module to send GraphQL requests and get information out of the response.

    Simple query

    Here, for example, is a simple playbook and accompanying GraphQL query to get the structure of a CICSplex with its CICS regions, and print out the result. The important part is the query key being added to the body of the request.

    playbook1.yml:

    - hosts: localhost
    
      tasks:
    
      - name: Get data from CICS
        register: result
        uri:
          url: https://my.cicsplex:12345/graphql/
          method: POST
          body_format: json
          body:
            query: '{{ lookup("file", "./queries/topology_query.graphql") }}' #  GraphQL query is passed here
    
      - name: Print out response
        debug:
          msg: '{{ result.json }}'
    

    queries/topology_query.graphql:

    {
      cicsplexes {
        name
        regions {
          name
        }
      }
    }
    

    Adding variables to the query

    Of course, you probably want to parameterise the query. You can do that using Jinja templating in Ansible. Here's a playbook and accompanying GraphQL query to find a particular region name (named MYREGION, in the vars) across all connected CICSplexes.

    playbook2.yml:

    - hosts: localhost
    
      vars:
        regionName: MYREGION
    
      tasks:
      - name: Get data from CICS
        register: result
        uri:
          url: https://my.cicsplex:12345/graphql/
          method: POST
          body_format: json
          body:
            query: '{{ lookup("template", "./templates/single_region_query.graphql.j2") }}' # template instead of plain file
    
      - name: Print out response
        debug:
          msg: '{{ result.json }}'
    

    templates/single_region_query.graphql.j2:

    {
      cicsplexes {
        name
        region(name: "{{ regionName }}") { # this variable will be expanded by Ansible
          name
        }
      }
    }
    

    Adding variables to the query - a better way

    However, I get a bit leery about templating. It seems fairly prone to injection problems, be they malicious or just accidental, when vars are coming in from elsewhere! Even writing the example above, I missed getting the quotes right. So I'd prefer to use GraphQL's built-in variable support to sanitise variables better.

    In CICS' CMCI GraphQL API, you can supply variables using the variables key in the body of the request you send to CICS, alongside the existing query key.

    Here you see the regionName variable being supplied in the body, and then the same variable (referred to as $regionName) in the GraphQL query.

    playbook3.yml:

    - hosts: localhost
    
      vars:
        regionName: MYREGION
    
      tasks:
    
      - name: Get data from plex
        register: result
        uri:
          url: https://my.cicsplex:12345/graphql/
          method: POST
          body_format: json
          body:
            query: '{{ lookup("file", "./queries/single_region_query.graphql") }}' # plain file, not template
            variables:
              regionName: "{{ regionName }}" # variables will get passed to CICS
    
      - name: Print out response
        debug:
          msg: '{{ result.json }}'
    

    queries/single_region_query.graphql:

    query searchForRegion ($regionName: String!) { # query declares used variables
      cicsplexes {
        name
        region(name: $regionName) { # GraphQL expands the variable here at query execution time
          name
        }
      }
    }