Using Ansible to connect to Oracle Dynamic Monitoring Service (DMS)

Using Ansible to connect to Oracle Dynamic Monitoring Service (DMS)

The Oracle Dynamic Monitoring Service (DMS) enables Oracle Fusion Middleware components to provide administration tools, such as Oracle Enterprise Manager, with data regarding the component's performance, state and on-going behaviour. It is the best tool to use if you want to have a scripting solution to monitor the states of your WebLogic domain servers, and all you need to have is the ability to connect over HTTP, and a username/password pair. Imagine that you could orchestrate a sequence of action to start/stop different components of your OFMW environment based on the state of other servers they are dependent upon.

If Ansible is a tool you use for orchestration of your environment startup, here is what you have to do:

  • login to a form based webpage
  • download a metric table of your choice as an xml file
  • use xpath to retrieve the text or attribute that you need

DMS application implements Java EE security container login method j_security_check for authentication. The way you login to DMS with Ansible uri module is to use the following url:

url: "http://{{ admin_host }}:{{ admin_port }}/dms/j_security_check"

The authentication form would expect two parameters: j_username and j_password, and upon successful authentication, it will return a status code that would be different depending on a version of Oracle FMW.

For 12c version you can define default variable:

stat_code: 303

For the 11g version, you have to use code 302. So, assuming that you have fmw_version variable defined with "11g" value:

- name: set status code
  set_fact:
    stat_code: 302
  when: fmw_version == 	'11g'

Once you logged in, you connect to DMS application using a previously stored cookie to retrieve an xml file. Your tasks would look like:

---
- name: login to DMS application
  uri:
    url: "http://{{ admin_host }}:{{ admin_port }}/dms/j_security_check"
    method: POST
    body_format: form-urlencoded
    body:
      j_username: "{{ admin_user }}"
      j_password: "{{ admin_pwd }}"
    status_code: "{{ stat_code }}"
  register: login
  
- name: DMS Spy table retrieval
  uri:
    url: "http://{{ admin_host }}:{{ admin_port }}/dms/index.html?format=xml&cache=false&prefetch=false&table={{ dmstbl }}&orderby={{ dmsord }}"
    method: GET
    return_content: yes
    dest: "{{ tmp_path }}/dms.xml"
    headers:
      Cookie: "{{ login.set_cookie }}"
 ...

If you want to get information about your servers' state, here is how you define your variables for the metric table:

dmstbl: weblogic.management.runtime.ServerRuntimeMBean
dmsord: Name

Finally, you have to use xml Ansible module to get the value of a metric you're looking for. Again, there would be a difference depending on a version of Oracle FMW. For 12c version, your resulting xml file uses namespaces that you have to present to xpath. Assuming that you store you WebLogic Admin server name in "admin_name" variable, and managed servers in "mservers" dictionary:

---
- name: "Read {{ admin_server }} status"
  xml:
    path: "{{ tmp_path }}/dms.xml"
    namespaces:
      ns: http://www.oracle.com/AS/collector
      xsi: http://www.w3.org/2001/XMLSchema-instance
    xpath: "/ns:tbml/ns:table/ns:row[ns:column/@name='{{ dmscolsearch }}' and ns:column[contains(.,'{{ admin_name }}')]]/ns:column[@name='{{ dmscoltarget }}']"
    content: text
  register: xmlresp_admin

- name: Read managed servers status
  xml:
    path: "{{ tmp_path }}/dms.xml"
    namespaces:
      ns: http://www.oracle.com/AS/collector
      xsi: http://www.w3.org/2001/XMLSchema-instance
    xpath: "/ns:tbml/ns:table/ns:row[ns:column/@name='{{ dmscolsearch }}' and ns:column[contains(.,'{{ server }}')]]/ns:column[@name='{{ dmscoltarget }}']"
    content: text
  register: xmlresp_managed
  ignore_errors: yes
  loop: "{{ mservers.keys()|trim }}"
  loop_control:
    loop_var: server
  
- name: Show admin server status
  debug:
    msg: "{{ admin_name }} status is {{ xmlresp_admin.matches[0]['{http://www.oracle.com/AS/collector}column'] }}"
  
- name: Show managed servers status:
  debug:
    msg: "{{ itr.server }} status is {{ itr.matches[0]['{http://www.oracle.com/AS/collector}column']|default('DOWN') }}"
  loop: "{{ xmlresp_managed.results }}"
  loop_control:
    loop_var: itr
    label: "{{ itr_server }}"
...

You would need to define the following variables for your xpath search to work, pointing it to a column attribute you're searching for a particular server name and a column with a resulting value:

dmscolsearch: Name
dmscoltarget: State

In the 11g version, which would be almost similar, you don't need to define namespaces.

---
- name: "Read {{ admin_server }} status"
  xml:
    path: "{{ tmp_path }}/dms.xml"
    xpath: "/tbml/table/row[column/@name='{{ dmscolsearch }}' and column[contains(.,'{{ admin_name }}')]]/column[@name='{{ dmscoltarget }}']"
    content: text
  register: xmlresp_admin

- name: Read managed servers status
  xml:
    path: "{{ tmp_path }}/dms.xml"
    xpath: "/tbml/table/row[column/@name='{{ dmscolsearch }}' and column[contains(.,'{{ server }}')]]/column[@name='{{ dmscoltarget }}']"
    content: text
  register: xmlresp_managed
  ignore_errors: yes
  loop: "{{ mservers.keys()|trim }}"
  loop_control:
    loop_var: server
  
- name: Show admin server status
  debug:
    msg: "{{ admin_name }} status is {{ xmlresp_admin.matches[0]['column'] }}"
  
- name: Show managed servers status:
  debug:
    msg: "{{ itr.server }} status is {{ itr.matches[0]['column']|default('DOWN') }}"
  loop: "{{ xmlresp_managed.results }}"
  loop_control:
    loop_var: itr
    label: "{{ itr_server }}"
...