Infrastructure at your Service

Olivier Spiesser

Ansible URI module and WebLogic REST API

By November 15, 2021 Ansible No Comments

I recently experienced REST API from WebLogic Server as I was trying to find a way to make configuration changes and metrics gatherings multi-servers and even multi-domains.
wlst can be used to achieve that, but from my point of view it has a big drawback: It is slow to initiate and connect to server.

Build Query URL

The difficult part of REST is to find the right URL you have to access via GET, POST or DELETE (…) to achieve your goal.

My first need was to update the Maximum Capacity of a JDBC DataSource.

curl \
-H X-Requested-By:MyClient \
-H Accept:application/json \
-u ${WL_USER}:${WL_ADMPWD} \
-X GET https://hostname:port/management/weblogic/latest/edit/JDBCSystemResources/MyJDBCDataSource/JDBCResource/JDBCConnectionPoolParams

I am using two headers:

  • X-Requested-By: Mandatory field for security reason (Cross Site Request Forgery)
  • Accept: Format of the result

Result example:

{
    "links": [
        {
            "rel": "parent",
            "href": "http:\/\/vm01:7001\/management\/weblogic\/latest\/edit\/JDBCSystemResources\/MyDBDatasource\/JDBCResource"
        },
        {
            "rel": "self",
            "href": "http:\/\/vm01:7001\/management\/weblogic\/latest\/edit\/JDBCSystemResources\/MyDBDatasource\/JDBCResource\/JDBCConnectionPoolParams"
        },
        {
            "rel": "canonical",
            "href": "http:\/\/vm01:7001\/management\/weblogic\/latest\/edit\/JDBCSystemResources\/MyDBDatasource\/JDBCResource\/JDBCConnectionPoolParams"
        }
    ],
    "identity": [
        "JDBCSystemResources",
        "MyDBDatasource",
        "JDBCResource",
        "JDBCConnectionPoolParams"
    ],
    "inactiveConnectionTimeoutSeconds": 0,
    "testConnectionsOnReserve": false,
    "wrapTypes": true,
    "fatalErrorCodes": null,
    "initialCapacity": 1,
    "pinnedToThread": false,
    "highestNumWaiters": 2147483647,
    "statementTimeout": -1,
    "countOfRefreshFailuresTillDisable": 2,
    "connectionHarvestMaxCount": 1,
    "profileHarvestFrequencySeconds": 300,
    "profileType": 0,
    "loginDelaySeconds": 0,
    "minCapacity": 10,
    "identityBasedConnectionPoolingEnabled": false,
    "connectionLabelingCallback": null,
    "countOfTestFailuresTillFlush": 2,
    "secondsToTrustAnIdlePoolConnection": 10,
    "shrinkFrequencySeconds": 900,
    "wrapJdbc": true,
    "connectionHarvestTriggerCount": -1,
    "credentialMappingEnabled": false,
    "connectionReserveTimeoutSeconds": 10,
    "removeInfectedConnections": true,
    "statementCacheSize": 10,
    "testTableName": "SQL SELECT 1 FROM DUAL",
    "JDBCXADebugLevel": 10,
    "maxCapacity": 100,
    "ignoreInUseConnectionsEnabled": true,
    "connectionCreationRetryFrequencySeconds": 0,
    "driverInterceptor": null,
    "testFrequencySeconds": 120,
    "profileConnectionLeakTimeoutSeconds": 0,
    "statementCacheType": "LRU",
    "initSql": null
}

As you can see the result is long and verbose, so to limit output to the interesting field (MaxCapacity), I modify the URL as follow:
https://serverhostname:port/management/weblogic/latest/edit/JDBCSystemResources/MyJDBCDataSource/JDBCResource/JDBCConnectionPoolParams?fields=maxCapacity&links=no
Then, new result is:

{"maxCapacity": 100}

Settings Modification

Modify the parameter with a simple curl:

curl\
-H X-Requested-By:MyClient \
-H Accept:application/json \
-H Content-Type:application/json \
-d "{ maxCapacity: 50}" \
-u ${WL_USER}:${WL_ADMPWD} \
-X POST https://hostname:port/management/weblogic/latest/JDBCSystemResources/MyJDBCDataSource/JDBCResource/JDBCConnectionPoolParams

Line 5 is the modified settings name (maxCapacity) with the new value (50).
An advantage of REST, as well as a “risk”, is that you don’t need to “lock and edit” configuration as we are used to with the web console.

As I am lately doing lots of Ansible coding, I told myself “Why not trying Ansible for this ?”. Luckily, a module named uri perfectly fit.

Ansible URI Module

As as first attempts, I want to proceed as follow keeping idempotency in focus:

  1. Call URL to check current value
  2. Display it
  3. Update if different

Playbook will look like this:

---
  - name: WebLogic REST playbook
    hosts: all
    gather_facts: no
    tasks:

      - name: 1. Check maxCapacity value
        uri:
          url: https://{{ inventory_hostname }}:7002/management/weblogic/latest/edit/JDBCSystemResources/MyJDBCDataSource/JDBCResource/JDBCConnectionPoolParams?fields=maxCapacity
          return_content: yes
          user: "{{ weblogic_adm_user }}"
          password: "{{ weblogic_adm_pwd }}"
        register: getResult

      - name: 2. Display Content Result
        ansible.builtin.debug:
          var: getResult.json.maxCapacity

      - name: 3. Set maxCapacity value if changed
        when: getResult.json.maxCapacity != newMaxCapacity
        uri:
          url: https://{{ inventory_hostname }}:7002/management/weblogic/latest/edit/JDBCSystemResources/MyJDBCDataSource/JDBCResource/JDBCConnectionPoolParams
          user: "{{ weblogic_adm_user }}"
          password: "{{ weblogic_adm_pwd }}"
          method: POST
          body_format: json
          body: 
            maxCapacity: "{{ newMaxCapacity }}"
          headers:
            Content-Type: application/json
            Accept: application/json
            X-Requested-By: MyAnsibleClient

Line 9: The requested URL
Line 10: return_content is the option to return result of the requested URL. register option will store it (line 13).
Line 20: This is the line which will modify maxCapacity only if it is different from current value.
Line 25: I have to specify the HTTP method and the format of the text body (line 26). URI module will do the conversion.
Line 29 to 32: HTTP headers definitions
Then, we need to run and see how this behave:
RESULT

Let’s try also with –check option:

PLAY [WebLogic REST playbook] *********************************************************************************************************************************************************************

TASK [Check maxCapacity value] ********************************************************************************************************************************************************************
skipping: [vm1]

TASK [Display Content Result] *********************************************************************************************************************************************************************
ok: [vm1] => {
    "changed": false,
    "getResult.json.maxCapacity": "VARIABLE IS NOT DEFINED!"
}

TASK [Set maxCapacity value] **********************************************************************************************************************************************************************
fatal: [vm1]: FAILED! => {"msg": "The conditional check 'getResult.json.maxCapacity != newMaxCapacity' failed. The error was: error while evaluating conditional (getResult.json.maxCapacity|int != {{ newMaxCapacity|int }}): 'dict object' has no attribute 'json'\n\nThe error appears to be in '/home/spiesser/updateJDBCMaxCapacity.yml': line 23, column 9, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n      - name: Set maxCapacity value\n        ^ here\n"}

PLAY RECAP ****************************************************************************************************************************************************************************************
vm1                  : ok=1    changed=0    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0   

As you can see, there are two errors. Why is that ? Because URI request of step 1 is skipped and a result, result is not populated. Then, step 2 fails to display unset variable and step 3 when condition evaluation fails as well.
To avoid that, I need to force step 1 to be run even when –check option is used. To achieve that, I need to add “check_mode: no” to step 1.
The output looks like that:

PLAY [WebLogic REST playbook] *********************************************************************************************************************************************************************

TASK [Check maxCapacity value] ********************************************************************************************************************************************************************
ok: [vm1]

TASK [Display Content Result] *********************************************************************************************************************************************************************
ok: [vm1] => {
    "changed": false,
    "getResult.json.maxCapacity": "50"
}

TASK [Set maxCapacity value] **********************************************************************************************************************************************************************
skipping: [vm1]

PLAY RECAP ****************************************************************************************************************************************************************************************
vm1                  : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

I can also display current value (step 2) only when running in check mode. then, I need to add a condition to the corresponding task

when: ansible_check_mode

Conclusion

It might look a bit overkill to use Ansible for that, but it makes REST API request easy to write and idempotency comes natively with Ansible.
Also, using REST remove the need of creating wlst scripts and thus managing these with Ansible templates.
Regarding performances, Ansible playbook takes 3 seconds whereas wlst takes 6 seconds (at second run, once libraries are cached).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Olivier Spiesser
Olivier Spiesser

Consultant