<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4896087204075281708</id><updated>2024-09-08T15:31:44.404+02:00</updated><category term="WebLogic"/><category term="Monitoring"/><category term="Automation"/><category term="WLST"/><category term="JVM"/><category term="Cluster"/><category term="Coherence"/><category term="Tuning"/><category term="Apache HTTP Server"/><category term="JMS"/><category term="Life Cycle Management"/><category term="Operating System"/><category term="Spring"/><category term="JBoss"/><category term="Database"/><category term="Security"/><category term="Class Loading"/><category term="Flight Recording"/><category term="Hibernate"/><category term="Java Mission Control"/><category term="OSB"/><category term="EJB"/><category term="Learning"/><category term="SOA"/><category term="WLDF"/><category term="GlassFish"/><category term="HAProxy"/><category term="JRockit Mission Control"/><category term="Memory Leak Detector"/><title type='text'>Middleware Snippets</title><subtitle type='html'>A blog that mediates between an application program and a network.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://middlewaresnippets.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default?max-results=5&amp;redirect=false'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default?start-index=6&amp;max-results=5&amp;redirect=false'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>133</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>5</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4896087204075281708.post-6561247541776534724</id><published>2020-05-21T11:53:00.007+02:00</published><updated>2022-08-18T15:51:22.246+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Automation"/><category scheme="http://www.blogger.com/atom/ns#" term="JBoss"/><category scheme="http://www.blogger.com/atom/ns#" term="JMS"/><category scheme="http://www.blogger.com/atom/ns#" term="Monitoring"/><category scheme="http://www.blogger.com/atom/ns#" term="WebLogic"/><title type='text'>From WildFly to WebLogic</title><content type='html'>In this post we are going to use the &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/jmsad/advance_config.html#GUID-D718ED25-ADD5-42FF-9DB7-ED37A178F43A&quot; target=&quot;_blank&quot;&gt;foreign server&lt;/a&gt; or the &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/brdge/index.html&quot; target=&quot;_blank&quot;&gt;messaging bridge&lt;/a&gt; to access third party JMS resources, such as &lt;a href=&quot;https://docs.wildfly.org/19.1/Admin_Guide.html#Messaging&quot; target=&quot;_blank&quot;&gt;activemq&lt;/a&gt;.

&lt;h2&gt;Set-Up WildFly&lt;/h2&gt;

First &lt;a href=&quot;https://wildfly.org/downloads/&quot; target=&quot;_blank&quot;&gt;download&lt;/a&gt; a WildFly distibution. In this example, we are going to use &lt;a href=&quot;https://download.jboss.org/wildfly/19.1.0.Final/wildfly-19.1.0.Final.zip&quot; target=&quot;_blank&quot;&gt;19.1.0.Final&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
To &lt;a href=&quot;https://docs.wildfly.org/19.1/Getting_Started_Guide.html#installation&quot; target=&quot;_blank&quot;&gt;install&lt;/a&gt; WildFly simply extract the download. As we are going to use messaging, we are going to use the &lt;a href=&quot;https://docs.wildfly.org/19.1/Getting_Started_Guide.html#wildfly-10-configurations&quot; target=&quot;_blank&quot;&gt;standalone-full.xml&lt;/a&gt; configuration.
&lt;br/&gt;&lt;br/&gt;
Before we can start WildFly, we edit a few things. First, we make sure that we can reach WildFly from other hosts

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;interfaces&amp;gt;
    &amp;lt;interface name=&quot;management&quot;&amp;gt;
        &amp;lt;nic name=&quot;eno16777736&quot;/&amp;gt;
    &amp;lt;/interface&amp;gt;
    &amp;lt;interface name=&quot;public&quot;&amp;gt;
        &amp;lt;nic name=&quot;eno16777736&quot;/&amp;gt;
    &amp;lt;/interface&amp;gt;
    &amp;lt;interface name=&quot;unsecure&quot;&amp;gt;
        &amp;lt;inet-address value=&quot;${jboss.bind.address.unsecure:127.0.0.1}&quot;/&amp;gt;
    &amp;lt;/interface&amp;gt;
&amp;lt;/interfaces&amp;gt;
&lt;/pre&gt;

Next, we add a &lt;a href=&quot;https://docs.wildfly.org/19.1/Admin_Guide.html#Default_HTTP_Interface_Security&quot; target=&quot;_blank&quot;&gt;managment user&lt;/a&gt; and an application user, the latter is needed when accessing messaging resources,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;subsystem xmlns=&quot;urn:jboss:domain:remoting:4.0&quot;&amp;gt;
    &amp;lt;http-connector name=&quot;http-remoting-connector&quot; connector-ref=&quot;default&quot; security-realm=&quot;ApplicationRealm&quot;/&amp;gt;
&amp;lt;/subsystem&amp;gt;

&amp;lt;subsystem xmlns=&quot;urn:jboss:domain:messaging-activemq:9.0&quot;&amp;gt;
    &amp;lt;server name=&quot;default&quot;&amp;gt;
        ...
        &amp;lt;security-setting name=&quot;#&quot;&amp;gt;
            &amp;lt;role name=&quot;guest&quot; send=&quot;true&quot; consume=&quot;true&quot; create-non-durable-queue=&quot;true&quot; delete-non-durable-queue=&quot;true&quot;/&amp;gt;
        &amp;lt;/security-setting&amp;gt;
        ...
    &amp;lt;/server&amp;gt;
&amp;lt;/subsystem&amp;gt;
&lt;/pre&gt;

To add users, we can use the &lt;a href=&quot;https://docs.wildfly.org/19.1/Admin_Guide.html#add-user-utility&quot; target=&quot;_blank&quot;&gt;add-user&lt;/a&gt; utility, for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[weblogic@javanode1 bin]$ ./add-user.sh

What type of user do you wish to add?
 a) Management User (mgmt-users.properties)
 b) Application User (application-users.properties)
(a): b

Enter the details of the new user to add.
Using realm &#39;ApplicationRealm&#39; as discovered from the existing property files.
Username : employee
Password recommendations are listed below. To modify these restrictions edit the add-user.properties configuration file.
 - The password should be different from the username
 - The password should not be one of the following restricted values {root, admin, administrator}
 - The password should contain at least 8 characters, 1 alphabetic character(s), 1 digit(s), 1 non-alphanumeric symbol(s)
Password :
WFLYDM0102: Password should have at least 1 non-alphanumeric symbol.
Are you sure you want to use the password entered yes/no? yes
Re-enter Password :
What groups do you want this user to belong to? (Please enter a comma separated list, or leave blank for none)[  ]: guest
About to add user &#39;employee&#39; for realm &#39;ApplicationRealm&#39;
Is this correct yes/no? yes
Added user &#39;employee&#39; to file &#39;/home/weblogic/wildfly/wildfly-19.1.0.Final/standalone/configuration/application-users.properties&#39;
Added user &#39;employee&#39; to file &#39;/home/weblogic/wildfly/wildfly-19.1.0.Final/domain/configuration/application-users.properties&#39;
Added user &#39;employee&#39; with groups guest to file &#39;/home/weblogic/wildfly/wildfly-19.1.0.Final/standalone/configuration/application-roles.properties&#39;
Added user &#39;employee&#39; with groups guest to file &#39;/home/weblogic/wildfly/wildfly-19.1.0.Final/domain/configuration/application-roles.properties&#39;
Is this new user going to be used for one AS process to connect to another AS process?
e.g. for a slave host controller connecting to the master or for a Remoting connection for server to server EJB calls.
yes/no? yes
To represent the user add the following to the server-identities definition &amp;lt;secret value=&quot;...&quot; /&amp;gt;
&lt;/pre&gt;

Note that before we can use the add-user utility we have to provide a &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable. To this end, we edit the &lt;code&gt;common.sh&lt;/code&gt; file (located in the &lt;code&gt;$WILDFLY_HOME/bin&lt;/code&gt; directory):

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#!/bin/sh -x

setModularJdk() { ... }

setDefaultModularJvmOptions() { ... }

# JDK 8
#JAVA_HOME=/u01/app/oracle/weblogic12.2.1/jdk
# JDK 11
JAVA_HOME=/u01/app/oracle/weblogic14.1.1/jdk
export JAVA_HOME
&lt;/pre&gt;

With the management and application user in place, we &lt;a href=&quot;https://docs.wildfly.org/19.1/Getting_Started_Guide.html#starting-wildfly-with-an-alternate-configuration&quot; target=&quot;_blank&quot;&gt;start&lt;/a&gt; the server:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[weblogic@javanode1 bin]$ ./standalone.sh --server-config=standalone-full.xml
=========================================================================

  JBoss Bootstrap Environment

  JBOSS_HOME: /home/weblogic/wildfly/wildfly-19.1.0.Final

  JAVA: /u01/app/oracle/weblogic14.1.1/jdk/bin/java

  JAVA_OPTS:  -server -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true  --add-exports=java.base/sun.nio.ch=ALL-UNNAMED --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED --add-exports=jdk.unsupported/sun.reflect=ALL-UNNAMED

=========================================================================
...
&lt;/pre&gt;

Next, we add a &lt;a href=&quot;https://docs.wildfly.org/19.1/Admin_Guide.html#Messaging&quot; target=&quot;_blank&quot;&gt;JMS queue&lt;/a&gt;:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[weblogic@javanode1 bin]$ ./jboss-cli.sh --connect --controller=remote+http://javanode1.ms.com:9990
[standalone@javanode1.ms.com:9990 /]

[standalone@javanode1.ms.com:9990 /] jms-queue --help --commands

[standalone@javanode1.ms.com:9990 /] jms-queue add --queue-address=testQueue --entries=jms/queue/test,java:jboss/exported/jms/queue/test

[standalone@javanode1.ms.com:9990 /] /subsystem=messaging-activemq/server=default/jms-queue=testQueue:read-resource
{
    &quot;outcome&quot; =&gt; &quot;success&quot;,
    &quot;result&quot; =&gt; {
        &quot;durable&quot; =&gt; true,
        &quot;entries&quot; =&gt; [
            &quot;jms/queue/test&quot;,
            &quot;java:jboss/exported/jms/queue/test&quot;
        ],
        &quot;legacy-entries&quot; =&gt; undefined,
        &quot;selector&quot; =&gt; undefined
    }
}

&amp;lt;subsystem xmlns=&quot;urn:jboss:domain:messaging-activemq:9.0&quot;&amp;gt;
    &amp;lt;server name=&quot;default&quot;&amp;gt;
        ...
        &amp;lt;jms-queue name=&quot;testQueue&quot; entries=&quot;jms/queue/test java:jboss/exported/jms/queue/test&quot;/&amp;gt;
        ...
        &amp;lt;connection-factory name=&quot;RemoteConnectionFactory&quot; entries=&quot;java:jboss/exported/jms/RemoteConnectionFactory&quot; connectors=&quot;http-connector&quot;/&amp;gt;
        ...
    &amp;lt;/server&amp;gt;
&amp;lt;/subsystem&amp;gt;
&lt;/pre&gt;

Note that when accessing resources remotely, the JNDI name has to start with &lt;code&gt;java:jboss/exported&lt;/code&gt; (see &lt;a href=&quot;https://docs.wildfly.org/17/Developer_Guide.html#JNDI_Remote_Reference&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).
&lt;br/&gt;&lt;br/&gt;
To test the set-up, we create a JMS client, for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
package test;

import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Properties;

public class Test {
    public static void main(String[] args) {
        final Properties env = new Properties();
        env.put(Context.INITIAL_CONTEXT_FACTORY, &quot;org.wildfly.naming.client.WildFlyInitialContextFactory&quot;);
        env.put(Context.PROVIDER_URL, &quot;http-remoting://javanode1.ms.com:8080&quot;);
        env.put(Context.SECURITY_PRINCIPAL, &quot;employee&quot;);
        env.put(Context.SECURITY_CREDENTIALS, &quot;...&quot;);
        env.put(&quot;jboss.naming.client.ejb.context&quot;, true);

        ConnectionFactory connectionFactory = null;
        Destination destination = null;

        try {
            InitialContext context = new InitialContext(env);

            connectionFactory = (ConnectionFactory) context.lookup(&quot;jms/RemoteConnectionFactory&quot;);
            destination = (Destination) context.lookup(&quot;jms/queue/test&quot;);

            System.out.println(connectionFactory);
            System.out.println(destination);

            sendMessage(connectionFactory, destination);
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    private static void sendMessage(ConnectionFactory connectionFactory, Destination destination) {
        Connection connection = null;
        Session session = null;
        MessageProducer messageProducer = null;

        try {
            connection = connectionFactory.createConnection(&quot;employee&quot;, &quot;...&quot;);
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            messageProducer = session.createProducer(destination);

            TextMessage text = session.createTextMessage();
            text.setText(&quot;Send some useful message&quot;);
            messageProducer.send(text);
        } catch (JMSException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException f) {
                    f.printStackTrace();
                }
            }
        }

    }
}
&lt;/pre&gt;

When we run the code above, the following is observed

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
May 21, 2020 9:49:36 AM org.wildfly.naming.client.Version &amp;lt;clinit&amp;gt;
INFO: WildFly Naming version 1.0.10.Final
May 21, 2020 9:49:36 AM org.wildfly.security.Version &amp;lt;clinit&amp;gt;
INFO: ELY00001: WildFly Elytron version 1.9.1.Final
May 21, 2020 9:49:36 AM org.xnio.Xnio &amp;lt;clinit&amp;gt;
INFO: XNIO version 3.7.2.Final
May 21, 2020 9:49:36 AM org.xnio.nio.NioXnio &amp;lt;clinit&amp;gt;
INFO: XNIO NIO Implementation Version 3.7.2.Final
May 21, 2020 9:49:36 AM org.jboss.threads.Version &amp;lt;clinit&amp;gt;
INFO: JBoss Threads version 2.3.3.Final
May 21, 2020 9:49:36 AM org.jboss.remoting3.EndpointImpl &amp;lt;clinit&amp;gt;
INFO: JBoss Remoting version 5.0.12.Final
SLF4J: Failed to load class &quot;org.slf4j.impl.StaticLoggerBinder&quot;.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
ActiveMQConnectionFactory [serverLocator=ServerLocatorImpl [initialConnectors=[TransportConfiguration(name=null, factory=org-apache-activemq-artemis-core-remoting-impl-netty-NettyConnectorFactory) ?httpUpgradeEndpoint=http-acceptor&amp;activemqServerName=default&amp;httpUpgradeEnabled=true&amp;port=8080&amp;host=192-168-101-110&amp;blockOnNonDurableSend=false&amp;retryIntervalMultiplier=1-0&amp;maxRetryInterval=2000&amp;producerMaxRate=-1&amp;blockOnDurableSend=true&amp;connectionTTL=60000&amp;compressLargeMessage=false&amp;reconnectAttempts=0&amp;ignoreJTA=false&amp;cacheLargeMessagesClient=false&amp;scheduledThreadPoolMaxSize=5&amp;useGlobalPools=true&amp;callFailoverTimeout=60000&amp;initialConnectAttempts=1&amp;clientFailureCheckPeriod=30000&amp;blockOnAcknowledge=false&amp;consumerWindowSize=1048576&amp;minLargeMessageSize=102400&amp;useTopologyForLoadBalancing=true&amp;finalizeChecks=false&amp;autoGroup=false&amp;threadPoolMaxSize=30&amp;confirmationWindowSize=-1&amp;transactionBatchSize=1048576&amp;callTimeout=30000&amp;preAcknowledge=false&amp;enable1xPrefixes=true&amp;cacheDestinations=false&amp;connectionLoadBalancingPolicyClassName=org-apache-activemq-artemis-api-core-client-loadbalance-RoundRobinConnectionLoadBalancingPolicy&amp;dupsOKBatchSize=1048576&amp;incomingInterceptorList=&amp;initialMessagePacketSize=1500&amp;consumerMaxRate=-1&amp;enableSharedClientID=true&amp;HA=false&amp;retryInterval=2000&amp;factoryType=0&amp;failoverOnInitialConnection=false&amp;outgoingInterceptorList=&amp;producerWindowSize=65536], discoveryGroupConfiguration=null], clientID=null, consumerWindowSize = 1048576, dupsOKBatchSize=1048576, transactionBatchSize=1048576, readOnly=falseEnableSharedClientID=true]
ActiveMQQueue[jms.queue.testQueue]

Process finished with exit code 0
&lt;/pre&gt;

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6M1bse_OHrt_q7Go3kCkBj2l-dKNU7dOQZH5eTyAzykHCbQqcM5zJkONUPmYsW7ob1REjCthE6IT1DxOYsYoC4lr7uXZT354ybBBQWZRo8RVp5UtLwGPuyTmG4oAPr1FXJzcJtrfBab8j/s640/wildfly_console_initial_runtime.png&quot; alt=&quot;wildfly_console_initial_runtime&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

&lt;h2&gt;Set-Up WebLogic&lt;/h2&gt;

Information to install and configure WebLogic can be found &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/index.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. In our case, we are going to use &lt;a href=&quot;https://docs.ansible.com/&quot; target=&quot;_blank&quot;&gt;Ansible&lt;/a&gt; to install and configure WebLogic, an example of which can found &lt;a href=&quot;http://middlewaresnippets.blogspot.com/search/label/Automation&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. When using JDK11 the JRE directoy no longer exists and the &lt;code&gt;java.security&lt;/code&gt; file is now located in the &lt;code&gt;$JAVA_HOME/conf/security&lt;/code&gt; directory.

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/ansible/roles/wls_application_vars/defaults/main.yml


##### SetEnvironmentVariables.sh #####
root_directory:            &quot;/u01&quot;

# Directory where the software to be installed is located
software_directory:        &quot;/mnt/hgfs/vmware_temp&quot;
    
# Name of JVM file that is used in the installation
#jvm_file_name:             &quot;jdk-8u231-linux-x64.tar.gz&quot;
jvm_file_name:             &quot;jdk-11.0.6_linux-x64_bin.tar.gz&quot;
    
# Name of the WebLogic file that is used in the installation
weblogic_file_name:        &quot;fmw_14.1.1.0.0_wls_generic.jar&quot;
...
##### files #####
set_user_overrides_sh:      &quot;{{ domain_configuration_home }}/bin/setUserOverrides.sh&quot;
set_domain_env_sh:          &quot;{{ domain_configuration_home }}/bin/setDomainEnv.sh&quot;
config_xml:                 &quot;{{ domain_configuration_home }}/config/config.xml&quot;
admin_server_lock_file:     &quot;{{ domain_configuration_home }}/servers/{{ admin_server_name }}/data/nodemanager/{{ admin_server_name }}.lck&quot;
#java_security:              &quot;{{ java_home }}/jre/lib/security/java.security&quot;
java_security:              &quot;{{ java_home }}/conf/security/java.security&quot;
...
##### domain parameters #####
# showAvailableTemplates(&#39;false&#39;,&#39;true&#39;,&#39;false&#39;)
weblogic_template_name:                          &quot;Basic WebLogic Server Domain&quot;
#weblogic_template_version:                       &quot;12.2.1.3&quot;
weblogic_template_version:                       &quot;14.1.1.0.0&quot;
...
&lt;/pre&gt;

When the Ansible playbooks are run the following output is observed

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[ansible@datanode ansible]$ ansible-playbook wls_admin_server.yml --inventory=inventories/production/wls_hosts.cfg

PLAY [wls_admin_server] ***********************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [javanode1]

TASK [wls_install : create root directory] ****************************************************************************************************************************************
ok: [javanode1]

TASK [wls_install : create directories] *******************************************************************************************************************************************
changed: [javanode1] =&amp;gt; (item=/u01/app/oracle/weblogic14.1.1/scripts)
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files)
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/templates)
skipping: [javanode1] =&amp;gt; (item=)
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/file_stores)
skipping: [javanode1] =&amp;gt; (item=)

TASK [wls_install : copy shell scripts] *******************************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : copy python scripts] ******************************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : create SetEnvironmentVariables.sh] ****************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : create environment.properties] ********************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : create silent install file] ***********************************************************************************************************************************
ok: [javanode1]

TASK [wls_install : create oraInst.loc] *******************************************************************************************************************************************
ok: [javanode1]

TASK [wls_install : install java virtual machine] *********************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : rename java virtual machine directory] ************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : set entropy gathering device] *********************************************************************************************************************************
changed: [javanode1]

TASK [wls_install : install weblogic server] **************************************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create create_basic_domain.py] ********************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create basic domain] ******************************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : debug basic domain creation] **********************************************************************************************************************
ok: [javanode1] =&amp;gt; {
    &quot;created_basic_domain.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CREATING DOMAIN&quot;,
        &quot;SELECTING TEMPLATES&quot;,
        &quot;LOADING TEMPLATES&quot;,
        &quot;SETTING DOMAIN OPTIONS&quot;,
        &quot;CHANGING ADMIN USER&quot;,
        &quot;SAVING DOMAIN&quot;,
        &quot;READING DOMAIN: /u01/app/oracle/weblogic14.1.1/configuration/domains/tryout_domain&quot;,
        &quot;SET NODE MANAGER CREDENTIALS&quot;,
        &quot;DISABLE HOSTNAME VERIFICATION&quot;,
        &quot;SET UP LDAP CONFIGURATION&quot;,
        &quot;ADJUSTING DOMAIN LOG&quot;,
        &quot;ADJUSTING ADMIN SERVER LOG&quot;,
        &quot;SAVING CHANGES&quot;,
        &quot;CREATING ADDITIONAL CONFIGURATION FILES&quot;,
        &quot;- CREATING boot.properties&quot;,
        &quot;WRITING FILE boot.properties&quot;
    ]
}

TASK [wls_config_admin_server : create application home directory] ****************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create nodemanager.properties] ********************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : disable derby flag] *******************************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create setUserOverrides.sh] ***********************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create nodemanager init script] *******************************************************************************************************************
skipping: [javanode1]

TASK [wls_config_admin_server : create nodemanager systemd script] ****************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : enable and start nodemanager service] *************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create store_user_config.py for node manager] *****************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create user configuration file and associated key file for node manager] **************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : debug user configuration file and associated key file creation for node manager] ******************************************************************
ok: [javanode1] =&amp;gt; {
    &quot;created_node_manager_user_configuration_file.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CREATING USER CONFIG FILES&quot;,
        &quot;- connecting to node manager&quot;,
        &quot;Connecting to Node Manager ...&quot;,
        &quot;Successfully Connected to Node Manager.&quot;,
        &quot;- creating user config file and associated key file for node manager&quot;,
        &quot;Currently connected to Node Manager to monitor the domain tryout_domain.&quot;,
        &quot;Creating the key file can reduce the security of your system if it is not kept in a secured location after it is created. Creating new key...&quot;,
        &quot;The username and password that were used for this WebLogic NodeManager connection are stored in /u01/app/oracle/weblogic14.1.1/scripts/.node_manager_config_file.properties and /u01/app/oracle/weblogic14.1.1/scripts/.node_manager_key_file.properties.&quot;,
        &quot;- disconnecting from node manager&quot;,
        &quot;Successfully disconnected from Node Manager.&quot;
    ]
}

TASK [wls_config_admin_server : start admin server] *******************************************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create store_user_config.py for admin server] *****************************************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : create user configuration file and associated key file for admin server] **************************************************************************
changed: [javanode1]

TASK [wls_config_admin_server : debug user configuration file and associated key file creation for admin server] ******************************************************************
ok: [javanode1] =&amp;gt; {
    &quot;created_admin_server_user_configuration_file.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CREATING USER CONFIG FILES&quot;,
        &quot;- connecting to admin server&quot;,
        &quot;Connecting to t3://javanode1.ms.com:7001 with userid weblogic ...&quot;,
        &quot;Successfully connected to Admin Server \&quot;AdminServer\&quot; that belongs to domain \&quot;tryout_domain\&quot;.&quot;,
        &quot;&quot;,
        &quot;Warning: An insecure protocol was used to connect to the server. &quot;,
        &quot;To ensure on-the-wire security, the SSL port or Admin port should be used instead.&quot;,
        &quot;&quot;,
        &quot;- creating user config file and associated key file for admin server&quot;,
        &quot;Creating the key file can reduce the security of your system if it is not kept in a secured location after it is created. Creating new key...&quot;,
        &quot;The username and password that were used for this WebLogic Server connection are stored in /u01/app/oracle/weblogic14.1.1/scripts/.admin_server_config_file.properties and /u01/app/oracle/weblogic14.1.1/scripts/.admin_server_key_file.properties.&quot;,
        &quot;- disconnecting from admin server&quot;,
        &quot;Disconnected from weblogic server: AdminServer&quot;
    ]
}

TASK [wls_config_admin_server : delete temporary files that contain usernames and passwords] **************************************************************************************
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/create_basic_domain.py)
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/store_user_config.py)

TASK [wls_config_deployment_environment : create create_deployment_environment.py] ************************************************************************************************
changed: [javanode1]

TASK [wls_config_deployment_environment : create deployment environment] **********************************************************************************************************
changed: [javanode1]

TASK [wls_config_deployment_environment : debug deployment environment creation] **************************************************************************************************
ok: [javanode1] =&amp;gt; {
    &quot;created_deployment_environment.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CONFIGURING DOMAIN&quot;,
        &quot;CONNECTING TO ADMIN SERVER&quot;,
        &quot;- using config and key file&quot;,
        &quot;Connecting to t3://javanode1.ms.com:7001 with userid weblogic ...&quot;,
        &quot;Successfully connected to Admin Server \&quot;AdminServer\&quot; that belongs to domain \&quot;tryout_domain\&quot;.&quot;,
        &quot;&quot;,
        &quot;Warning: An insecure protocol was used to connect to the server. &quot;,
        &quot;To ensure on-the-wire security, the SSL port or Admin port should be used instead.&quot;,
        &quot;&quot;,
        &quot;START EDIT MODE&quot;,
        &quot;CREATING DYNAMIC CLUSTER&quot;,
        &quot;- creating dynamic cluster application_cluster&quot;,
        &quot;- creating server template application_cluster_server_template&quot;,
        &quot; - configuring overload protection&quot;,
        &quot; - configuring logging&quot;,
        &quot;- adding server template to the cluster application_cluster&quot;,
        &quot;CREATING MACHINES&quot;,
        &quot;- creating unix machine machine_javanode1&quot;,
        &quot; - configuring node manager&quot;,
        &quot;- creating unix machine machine_javanode2&quot;,
        &quot; - configuring node manager&quot;,
        &quot;SAVE AND ACTIVATE CHANGES&quot;,
        &quot;- changes that are activated:&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: Clusters, modification: create, old value: None, new value: application_cluster&quot;,
        &quot; - bean: com.bea:Name=application_cluster,Type=Cluster, attribute: NumberOfServersInClusterAddress, modification: modify, old value: None, new value: 2&quot;,
        &quot; - bean: com.bea:Name=application_cluster,Type=Cluster, attribute: WeblogicPluginEnabled, modification: modify, old value: None, new value: True&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: Machines, modification: create, old value: None, new value: machine_javanode1&quot;,
        &quot; - bean: com.bea:Name=machine_javanode1,Type=UnixMachine, attribute: PostBindUID, modification: modify, old value: None, new value: weblogic&quot;,
        &quot; - bean: com.bea:Name=machine_javanode1,Type=UnixMachine, attribute: PostBindUIDEnabled, modification: modify, old value: None, new value: True&quot;,
        &quot; - bean: com.bea:Name=machine_javanode1,Type=UnixMachine, attribute: PostBindGIDEnabled, modification: modify, old value: None, new value: True&quot;,
        &quot; - bean: com.bea:Name=machine_javanode1,Type=UnixMachine, attribute: PostBindGID, modification: modify, old value: None, new value: javainstall&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: Machines, modification: create, old value: None, new value: machine_javanode2&quot;,
        &quot; - bean: com.bea:Name=machine_javanode2,Type=UnixMachine, attribute: PostBindUID, modification: modify, old value: None, new value: weblogic&quot;,
        &quot; - bean: com.bea:Name=machine_javanode2,Type=UnixMachine, attribute: PostBindUIDEnabled, modification: modify, old value: None, new value: True&quot;,
        &quot; - bean: com.bea:Name=machine_javanode2,Type=UnixMachine, attribute: PostBindGIDEnabled, modification: modify, old value: None, new value: True&quot;,
        &quot; - bean: com.bea:Name=machine_javanode2,Type=UnixMachine, attribute: PostBindGID, modification: modify, old value: None, new value: javainstall&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: ServerTemplates, modification: create, old value: None, new value: application_cluster_server_template&quot;,
        &quot; - bean: com.bea:Name=application_cluster_server_template,Type=ServerTemplate, attribute: Cluster, modification: modify, old value: None, new value: application_cluster&quot;,
        &quot; - bean: com.bea:Name=application_cluster_server_template,Type=ServerTemplate, attribute: ListenPort, modification: modify, old value: None, new value: 9001&quot;,
        &quot;- status of this activation per server:&quot;,
        &quot; - server: AdminServer, status: committed&quot;,
        &quot;- checking if admin server needs to be restarted, and checking if managed servers are affected by the change, if so these will be shutdown&quot;,
        &quot;START EDIT MODE&quot;,
        &quot;CREATING CLUSTERED MESSAGING MODULE&quot;,
        &quot;- creating file store application_cluster_filestore&quot;,
        &quot;- creating jms server application_cluster_jms_server&quot;,
        &quot;- creating jms system module&quot;,
        &quot; - creating sub deployment jms_system_resource_module_sub_deployment&quot;,
        &quot; - setting sub deployment targets based on available jms server&quot;,
        &quot;CREATING MESSAGING RESOURCES&quot;,
        &quot;- creating connection factories&quot;,
        &quot; - creating connection factory connection_factory&quot;,
        &quot;- creating uniform distributed queues&quot;,
        &quot; - creating uniform distributed queue company_distributed_queue&quot;,
        &quot; - creating error destination company_distributed_error_queue&quot;,
        &quot;CREATING DATA SOURCES&quot;,
        &quot;- creating jdbc system resource&quot;,
        &quot; - setting data source parameters&quot;,
        &quot; - setting jdbc driver parameters&quot;,
        &quot; - setting jdbc connection pool parameters&quot;,
        &quot;SAVE AND ACTIVATE CHANGES&quot;,
        &quot;- changes that are activated:&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: FileStores, modification: create, old value: None, new value: application_cluster_filestore&quot;,
        &quot; - bean: com.bea:Name=application_cluster_filestore,Type=FileStore, attribute: Directory, modification: modify, old value: None, new value: /mnt/hgfs/vmware_temp/file_stores&quot;,
        &quot; - bean: com.bea:Name=application_cluster_filestore,Type=FileStore, attribute: Targets, modification: add, old value: None, new value: application_cluster&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: JDBCSystemResources, modification: create, old value: None, new value: data_source&quot;,
        &quot; - bean: com.bea:Name=data_source,Type=JDBCSystemResource, attribute: Targets, modification: add, old value: None, new value: application_cluster&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: JMSServers, modification: create, old value: None, new value: application_cluster_jms_server&quot;,
        &quot; - bean: com.bea:Name=application_cluster_jms_server,Type=JMSServer, attribute: PersistentStore, modification: modify, old value: None, new value: application_cluster_filestore&quot;,
        &quot; - bean: com.bea:Name=application_cluster_jms_server,Type=JMSServer, attribute: Targets, modification: add, old value: None, new value: application_cluster&quot;,
        &quot; - bean: com.bea:Name=tryout_domain,Type=Domain, attribute: JMSSystemResources, modification: create, old value: None, new value: jms_system_resource_module&quot;,
        &quot; - bean: com.bea:Name=jms_system_resource_module,Type=JMSSystemResource, attribute: Targets, modification: add, old value: None, new value: application_cluster&quot;,
        &quot;- status of this activation per server:&quot;,
        &quot; - server: AdminServer, status: committed&quot;,
        &quot;- checking if admin server needs to be restarted, and checking if managed servers are affected by the change, if so these will be shutdown&quot;,
        &quot;DISCONNECT FROM THE ADMIN SERVER&quot;,
        &quot;Disconnected from weblogic server: AdminServer&quot;
    ]
}

TASK [wls_config_deployment_environment : remove old template] ********************************************************************************************************************
changed: [javanode1]

TASK [wls_config_deployment_environment : create new template] ********************************************************************************************************************
changed: [javanode1]

TASK [wls_config_deployment_environment : delete temporary files that contain usernames and passwords] ****************************************************************************
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/create_deployment_environment.py)

TASK [wls_application_config : create application_config.py] **********************************************************************************************************************
changed: [javanode1]

TASK [wls_application_config : execute application_config.py] *********************************************************************************************************************
changed: [javanode1]

TASK [wls_application_config : debug application config] **************************************************************************************************************************
ok: [javanode1] =&amp;gt; {
    &quot;executed_application_config.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CONFIGURING DOMAIN&quot;,
        &quot;CONNECTING TO ADMIN SERVER&quot;,
        &quot;- using config and key file&quot;,
        &quot;Connecting to t3://javanode1.ms.com:7001 with userid weblogic ...&quot;,
        &quot;Successfully connected to Admin Server \&quot;AdminServer\&quot; that belongs to domain \&quot;tryout_domain\&quot;.&quot;,
        &quot;&quot;,
        &quot;Warning: An insecure protocol was used to connect to the server. &quot;,
        &quot;To ensure on-the-wire security, the SSL port or Admin port should be used instead.&quot;,
        &quot;&quot;,
        &quot;START EDIT MODE&quot;,
        &quot;CREATING WORK MANAGERS&quot;,
        &quot;CONFIGURING ENVIRONMENT&quot;,
        &quot;SAVE AND ACTIVATE CHANGES&quot;,
        &quot;- no changes&quot;,
        &quot;CREATING USERS AND GROUPS&quot;,
        &quot;DISCONNECT FROM THE ADMIN SERVER&quot;,
        &quot;Disconnected from weblogic server: AdminServer&quot;
    ]
}

TASK [wls_application_config : delete temporary files that contain usernames and passwords] ***************************************************************************************
changed: [javanode1] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/application_config.py)

PLAY RECAP ************************************************************************************************************************************************************************
javanode1                  : ok=40   changed=31   unreachable=0    failed=0
&lt;/pre&gt;

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[ansible@datanode ansible]$ ansible-playbook wls_managed_servers.yml --inventory=inventories/production/wls_hosts.cfg

PLAY [wls_managed_servers] ********************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************
ok: [javanode2]

TASK [wls_install : create root directory] ****************************************************************************************************************************************
ok: [javanode2]

TASK [wls_install : create directories] *******************************************************************************************************************************************
changed: [javanode2] =&amp;gt; (item=/u01/app/oracle/weblogic14.1.1/scripts)
changed: [javanode2] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files)
changed: [javanode2] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/templates)
skipping: [javanode2] =&amp;gt; (item=)
changed: [javanode2] =&amp;gt; (item=/mnt/hgfs/vmware_temp/file_stores)
skipping: [javanode2] =&amp;gt; (item=)

TASK [wls_install : copy shell scripts] *******************************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : copy python scripts] ******************************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : create SetEnvironmentVariables.sh] ****************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : create environment.properties] ********************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : create silent install file] ***********************************************************************************************************************************
ok: [javanode2]

TASK [wls_install : create oraInst.loc] *******************************************************************************************************************************************
ok: [javanode2]

TASK [wls_install : install java virtual machine] *********************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : rename java virtual machine directory] ************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : set entropy gathering device] *********************************************************************************************************************************
changed: [javanode2]

TASK [wls_install : install weblogic server] **************************************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create domain] *********************************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create application home directory] *************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create nodemanager.properties] *****************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : disable derby flag] ****************************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create setUserOverrides.sh] ********************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create nodemanager init script] ****************************************************************************************************************
skipping: [javanode2]

TASK [wls_config_managed_servers : create nodemanager systemd script] *************************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : enable and start nodemanager service] **********************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create store_user_config.py for node manager] **************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create user configuration file and associated key file for node manager] ***********************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : debug user configuration file and associated key file creation for node manager] ***************************************************************
ok: [javanode2] =&amp;gt; {
    &quot;created_node_manager_user_configuration_file.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Jython scans all the jar files it can find at first startup. Depending on the system, this process may take a few minutes to complete, and WLST may not return a prompt right away.&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CREATING USER CONFIG FILES&quot;,
        &quot;- connecting to node manager&quot;,
        &quot;Connecting to Node Manager ...&quot;,
        &quot;Successfully Connected to Node Manager.&quot;,
        &quot;- creating user config file and associated key file for node manager&quot;,
        &quot;Currently connected to Node Manager to monitor the domain tryout_domain.&quot;,
        &quot;Creating the key file can reduce the security of your system if it is not kept in a secured location after it is created. Creating new key...&quot;,
        &quot;The username and password that were used for this WebLogic NodeManager connection are stored in /u01/app/oracle/weblogic14.1.1/scripts/.node_manager_config_file.properties and /u01/app/oracle/weblogic14.1.1/scripts/.node_manager_key_file.properties.&quot;,
        &quot;- disconnecting from node manager&quot;,
        &quot;Successfully disconnected from Node Manager.&quot;
    ]
}

TASK [wls_config_managed_servers : create store_user_config.py for admin server] **************************************************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : create user configuration file and associated key file for admin server] ***********************************************************************
changed: [javanode2]

TASK [wls_config_managed_servers : debug user configuration file and associated key file creation for admin server] ***************************************************************
ok: [javanode2] =&amp;gt; {
    &quot;created_admin_server_user_configuration_file.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CREATING USER CONFIG FILES&quot;,
        &quot;- connecting to admin server&quot;,
        &quot;Connecting to t3://javanode1.ms.com:7001 with userid weblogic ...&quot;,
        &quot;Successfully connected to Admin Server \&quot;AdminServer\&quot; that belongs to domain \&quot;tryout_domain\&quot;.&quot;,
        &quot;&quot;,
        &quot;Warning: An insecure protocol was used to connect to the server. &quot;,
        &quot;To ensure on-the-wire security, the SSL port or Admin port should be used instead.&quot;,
        &quot;&quot;,
        &quot;- creating user config file and associated key file for admin server&quot;,
        &quot;Creating the key file can reduce the security of your system if it is not kept in a secured location after it is created. Creating new key...&quot;,
        &quot;The username and password that were used for this WebLogic Server connection are stored in /u01/app/oracle/weblogic14.1.1/scripts/.admin_server_config_file.properties and /u01/app/oracle/weblogic14.1.1/scripts/.admin_server_key_file.properties.&quot;,
        &quot;- disconnecting from admin server&quot;,
        &quot;Disconnected from weblogic server: AdminServer&quot;
    ]
}

TASK [wls_config_managed_servers : delete temporary files that contain usernames and passwords] ***********************************************************************************
changed: [javanode2] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/store_user_config.py)

TASK [wls_application_config : create application_config.py] **********************************************************************************************************************
changed: [javanode2]

TASK [wls_application_config : execute application_config.py] *********************************************************************************************************************
changed: [javanode2]

TASK [wls_application_config : debug application config] **************************************************************************************************************************
ok: [javanode2] =&amp;gt; {
    &quot;executed_application_config.stdout_lines&quot;: [
        &quot;&quot;,
        &quot;Initializing WebLogic Scripting Tool (WLST) ...&quot;,
        &quot;&quot;,
        &quot;Welcome to WebLogic Server Administration Scripting Shell&quot;,
        &quot;&quot;,
        &quot;Type help() for help on available commands&quot;,
        &quot;&quot;,
        &quot;CREATING VARIABLES FROM PROPERTIES FILE&quot;,
        &quot;CREATING VARIABLES FROM ENVIRONMENT VARIABLES&quot;,
        &quot;CREATING ADDITIONAL VARIABLES&quot;,
        &quot;CONFIGURING DOMAIN&quot;,
        &quot;CONNECTING TO ADMIN SERVER&quot;,
        &quot;- using config and key file&quot;,
        &quot;Connecting to t3://javanode1.ms.com:7001 with userid weblogic ...&quot;,
        &quot;Successfully connected to Admin Server \&quot;AdminServer\&quot; that belongs to domain \&quot;tryout_domain\&quot;.&quot;,
        &quot;&quot;,
        &quot;Warning: An insecure protocol was used to connect to the server. &quot;,
        &quot;To ensure on-the-wire security, the SSL port or Admin port should be used instead.&quot;,
        &quot;&quot;,
        &quot;START EDIT MODE&quot;,
        &quot;CREATING WORK MANAGERS&quot;,
        &quot;CONFIGURING ENVIRONMENT&quot;,
        &quot;SAVE AND ACTIVATE CHANGES&quot;,
        &quot;- no changes&quot;,
        &quot;CREATING USERS AND GROUPS&quot;,
        &quot;- user employee already exists and will not be created&quot;,
        &quot;- user manager already exists and will not be created&quot;,
        &quot;DISCONNECT FROM THE ADMIN SERVER&quot;,
        &quot;Disconnected from weblogic server: AdminServer&quot;
    ]
}

TASK [wls_application_config : delete temporary files that contain usernames and passwords] ***************************************************************************************
changed: [javanode2] =&amp;gt; (item=/mnt/hgfs/vmware_temp/application_files/application_config.py)

PLAY RECAP ************************************************************************************************************************************************************************
javanode2                  : ok=31   changed=24   unreachable=0    failed=0
&lt;/pre&gt;

&lt;h3&gt;Foreign Server&lt;/h3&gt;
WebLogic JMS allows us to reference third-party JMS providers within a local WebLogic Server JNDI tree. With Foreign Server resources in JMS modules, we can quickly map a foreign JMS provider so that its associated connection factories and destinations appear in the WebLogic JNDI tree as local JMS objects. More information can be found &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/jmsad/advance_config.html#GUID-D718ED25-ADD5-42FF-9DB7-ED37A178F43A&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
To configure a foreign server we follow the steps presented &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/wlach/taskhelp/jms_modules/foreign_servers/ConfigureForeignServers.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. A few things are worth mentioning:

&lt;ul&gt;
    &lt;li&gt;If the Foreign JMS Server expects a user credential to be passed when making a JMS connection then it should be defined under the foreign connection factory configuration.&lt;/li&gt;
    &lt;li&gt;Similarly if the Foreign JMS Server expects a user credential to be passed even for JNDI lookup then the user credentials should be configured under JNDI as well. Note that the username is to be specified using a JNDI property, in this case the JNDI property &lt;code&gt;java.naming.security.principal&lt;/code&gt;. Also provide any other properties that your JMS provider expects under JNDI Properties. As we are not accessing EJBs, we do not need to specify the &lt;code&gt;jboss.naming.client.ejb.context&lt;/code&gt; property (see &lt;a href=&quot;https://docs.wildfly.org/17/Developer_Guide.html#Remote_EJB_invocations_via_JNDI_-_EJB_client_API_or_remote-naming_project&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).&lt;/li&gt;
    &lt;li&gt;To be able to use the &lt;code&gt;WildFlyInitialContextFactory&lt;/code&gt; and the &lt;code&gt;http-remoting&lt;/code&gt; protocol, we need to add the &lt;code&gt;$WILDFLY_HOME/bin/client/jboss-client.jar&lt;/code&gt; to WebLogic&#39;s class path. This can be accomplished by adding the jar file to WebLogic&#39;s &lt;code&gt;$DOMAIN_HOME/lib&lt;/code&gt; directory.&lt;/li&gt;
&lt;/ul&gt;

The configuration of the foreign server looks as follows:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# machine
&amp;lt;machine xsi:type=&quot;unix-machineType&quot;&amp;gt;
    &amp;lt;name&amp;gt;machine_javanode1&amp;lt;/name&amp;gt;
    &amp;lt;node-manager&amp;gt;
        &amp;lt;nm-type&amp;gt;Plain&amp;lt;/nm-type&amp;gt;
        &amp;lt;listen-address&amp;gt;javanode1.ms.com&amp;lt;/listen-address&amp;gt;
        &amp;lt;listen-port&amp;gt;5550&amp;lt;/listen-port&amp;gt;
    &amp;lt;/node-manager&amp;gt;
    &amp;lt;post-bind-uid-enabled&amp;gt;true&amp;lt;/post-bind-uid-enabled&amp;gt;
    &amp;lt;post-bind-uid&amp;gt;weblogic&amp;lt;/post-bind-uid&amp;gt;
    &amp;lt;post-bind-gid-enabled&amp;gt;true&amp;lt;/post-bind-gid-enabled&amp;gt;
    &amp;lt;post-bind-gid&amp;gt;javainstall&amp;lt;/post-bind-gid&amp;gt;
&amp;lt;/machine&amp;gt;

# managed server
&amp;lt;server&amp;gt;
    &amp;lt;name&amp;gt;testserver&amp;lt;/name&amp;gt;
    &amp;lt;ssl&amp;gt;
        &amp;lt;enabled&amp;gt;false&amp;lt;/enabled&amp;gt;
    &amp;lt;/ssl&amp;gt;
    &amp;lt;machine&amp;gt;machine_javanode1&amp;lt;/machine&amp;gt;
    &amp;lt;listen-port&amp;gt;10080&amp;lt;/listen-port&amp;gt;
    &amp;lt;listen-port-enabled&amp;gt;true&amp;lt;/listen-port-enabled&amp;gt;
    &amp;lt;cluster xsi:nil=&quot;true&quot;&amp;gt;&amp;lt;/cluster&amp;gt;
    &amp;lt;web-server&amp;gt;
        &amp;lt;web-server-log&amp;gt;
            &amp;lt;number-of-files-limited&amp;gt;false&amp;lt;/number-of-files-limited&amp;gt;
        &amp;lt;/web-server-log&amp;gt;
    &amp;lt;/web-server&amp;gt;
    &amp;lt;listen-address&amp;gt;javanode1.ms.com&amp;lt;/listen-address&amp;gt;
    &amp;lt;java-compiler&amp;gt;javac&amp;lt;/java-compiler&amp;gt;
    &amp;lt;client-cert-proxy-enabled&amp;gt;false&amp;lt;/client-cert-proxy-enabled&amp;gt;
    &amp;lt;server-diagnostic-config&amp;gt;
        &amp;lt;wldf-diagnostic-volume&amp;gt;Low&amp;lt;/wldf-diagnostic-volume&amp;gt;
    &amp;lt;/server-diagnostic-config&amp;gt;
&amp;lt;/server&amp;gt;

# jms system resource
&amp;lt;jms-system-resource&amp;gt;
    &amp;lt;name&amp;gt;testmodule&amp;lt;/name&amp;gt;
    &amp;lt;target&amp;gt;testserver&amp;lt;/target&amp;gt;
    &amp;lt;descriptor-file-name&amp;gt;jms/testmodule-jms.xml&amp;lt;/descriptor-file-name&amp;gt;
&amp;lt;/jms-system-resource&amp;gt;

# foreign server
&amp;lt;weblogic-jms ...&amp;gt;
    &amp;lt;foreign-server name=&quot;testforeignserver&quot;&amp;gt;
        &amp;lt;default-targeting-enabled&amp;gt;true&amp;lt;/default-targeting-enabled&amp;gt;
        &amp;lt;foreign-destination name=&quot;TestForeignDestination&quot;&amp;gt;
            &amp;lt;local-jndi-name&amp;gt;jms/TestForeignDestination&amp;lt;/local-jndi-name&amp;gt;
            &amp;lt;remote-jndi-name&amp;gt;jms/queue/test&amp;lt;/remote-jndi-name&amp;gt;
        &amp;lt;/foreign-destination&amp;gt;
        &amp;lt;foreign-connection-factory name=&quot;TestForeignConnectionFactory-0&quot;&amp;gt;
            &amp;lt;local-jndi-name&amp;gt;jms/TestForeignConnectionFactory-0&amp;lt;/local-jndi-name&amp;gt;
            &amp;lt;remote-jndi-name&amp;gt;jms/RemoteConnectionFactory&amp;lt;/remote-jndi-name&amp;gt;
            &amp;lt;username&amp;gt;employee&amp;lt;/username&amp;gt;
            &amp;lt;password-encrypted&amp;gt;...&amp;lt;/password-encrypted&amp;gt;
            &amp;lt;connection-health-checking&amp;gt;enabled&amp;lt;/connection-health-checking&amp;gt;
        &amp;lt;/foreign-connection-factory&amp;gt;
        &amp;lt;initial-context-factory&amp;gt;org.wildfly.naming.client.WildFlyInitialContextFactory&amp;lt;/initial-context-factory&amp;gt;
        &amp;lt;connection-url&amp;gt;http-remoting://javanode1.ms.com:8080&amp;lt;/connection-url&amp;gt;
        &amp;lt;jndi-properties-credential-encrypted&amp;gt;...&amp;lt;/jndi-properties-credential-encrypted&amp;gt;
        &amp;lt;jndi-property&amp;gt;
            &amp;lt;key&amp;gt;java.naming.security.principal&amp;lt;/key&amp;gt;
            &amp;lt;value&amp;gt;employee&amp;lt;/value&amp;gt;
        &amp;lt;/jndi-property&amp;gt;
    &amp;lt;/foreign-server&amp;gt;
&amp;lt;/weblogic-jms&amp;gt;
&lt;/pre&gt;

Next start the managed server, and check the JNDI tree of the managed server

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZdOlvg4kIRtsR7ddWM4T1AVbc3Lkzp-E5XuSu-0rOy6di3Mhwqer0mr4SqRs4l7oBfwtgJdF8Jr63s80P41zqY_k2rUeDbOg1uu7EMQnzQ7j2gdDUUo6I_wzn6LRc1EpBiLAj6haIeNP5/s640/jndi_tree.png&quot; alt=&quot;jndi_tree&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

To consume messages, we create a message-driven bean (example can be found in the &lt;a href=&quot;https://javaee.github.io/tutorial/toc.html&quot; target=&quot;_blank&quot;&gt;JavaEE Tutorial&lt;/a&gt;).

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
package logic;

import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.*;

@MessageDriven(name = &quot;ReceiveMDB&quot;, activationConfig = {
        @ActivationConfigProperty(propertyName = &quot;destinationType&quot;, propertyValue = &quot;javax.jms.Queue&quot;),
        @ActivationConfigProperty(propertyName = &quot;destinationLookup&quot;, propertyValue = &quot;jms/TestForeignDestination&quot;),
        @ActivationConfigProperty(propertyName = &quot;connectionFactoryLookup&quot;, propertyValue = &quot;jms/TestForeignConnectionFactory-0&quot;)
})
public class ReceiveMDB implements MessageListener {
    @Override
    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            try {
                System.out.println(&quot;RECEIVED TEXT MESSAGE &quot; + textMessage.getText());
            } catch (JMSException e) {
                e.printStackTrace();
            }
        }
    }
}
&lt;/pre&gt;

The &lt;a href=&quot;https://javaee.github.io/javaee-spec/javadocs/javax/ejb/ActivationConfigProperty.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;ActivationConfigProperty&lt;/code&gt;&lt;/a&gt; provides the connection information:

&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;destinationType&lt;/code&gt;. This property is used to specify whether the message driven bean is intended to be used with a queue or a topic. The value must be either &lt;code&gt;javax.jms.Queue&lt;/code&gt; or &lt;code&gt;javax.jms.Topic&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;destinationLookup&lt;/code&gt;. This property is used to specify the JMS queue or topic from which a JMS message-driven bean is to receive messages.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;connectionFactoryLookup&lt;/code&gt;. This property is used to specify the JMS connection factory that will be used to connect to the JMS provider from which a JMS message-driven bean is to receive messages.&lt;/li&gt;
&lt;/ul&gt;

Start the managed server and deploy the message-driven bean.

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGiMzPkxetzUFwntRCCslsPYGVPmac-JJq_h7IXXWSAAm1yI_XhBDzSiNLfCcrz-7FE2kOLI4pZLLpQVMLCtOsvYjm_JF0_a6Q1gQG-Tb6w-iNnVrLHK_Hsp5zhLK6yJgL628zfySVKWu8/s640/wildfly_mdb_connected_and_consumed_message.png&quot; alt=&quot;wildfly_mdb_connected_and_consumed_message&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZNF8k9vARIsfK5RIkVEeZ4WQt8N2VvDFY9cSEVM04nkYdgjbhOsVRe6vORVCx3fVoYSie2QGYf2y9ZmV18cF4A8dHmCtC5It-Bmg3ulkuY4Skj_9LtuRj5K-CGywr-AeHBOzKDsicjyUp/s640/mdb_monitoring.png&quot; alt=&quot;mdb_monitoring&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

Note that by default WebLogic creates 16 consumer, this can edited by using the &lt;code&gt;META-INF/weblogic-ejb-jar.xml&lt;/code&gt;, for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;weblogic-ejb-jar ...&amp;gt;
    &amp;lt;weblogic-enterprise-bean&amp;gt;
        &amp;lt;ejb-name&amp;gt;ReceiveMDB&amp;lt;/ejb-name&amp;gt;
        &amp;lt;message-driven-descriptor&amp;gt;
            &amp;lt;pool&amp;gt;
                &amp;lt;max-beans-in-free-pool&amp;gt;1&amp;lt;/max-beans-in-free-pool&amp;gt;
                &amp;lt;initial-beans-in-free-pool&amp;gt;1&amp;lt;/initial-beans-in-free-pool&amp;gt;
            &amp;lt;/pool&amp;gt;
        &amp;lt;/message-driven-descriptor&amp;gt;
    &amp;lt;/weblogic-enterprise-bean&amp;gt;
&amp;lt;/weblogic-ejb-jar&amp;gt;
&lt;/pre&gt;

&lt;h2&gt;Messaging Bridge&lt;/h2&gt;
The &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/brdge/basics.html#GUID-9A0D179A-65D6-4DC9-B187-20126B231835&quot; target=&quot;_blank&quot;&gt;Messaging Bridge&lt;/a&gt; is a forwarding mechanism that provides interoperability between WebLogic JMS implementations. A messaging bridge also provides interoperability between JMS and other messaging products.
&lt;br/&gt;&lt;br/&gt;
Use the Messaging Bridge to integrate your messaging applications between:

&lt;ul&gt;
    &lt;li&gt;Any two implementations of WebLogic JMS, including those from separate releases of WebLogic Server.&lt;/li&gt;
    &lt;li&gt;WebLogic JMS implementations that reside in separate WebLogic domains.&lt;/li&gt;
    &lt;li&gt;WebLogic JMS and a third-party JMS product (for example, MQSeries).&lt;/li&gt;
&lt;/ul&gt;

A messaging bridge instance forwards messages between a pair of bridge source and target destinations. These destinations are mapped to a pair of bridge source and target destinations. The messaging bridge reads messages from the source bridge destination and forwards those messages to the target bridge destination. For WebLogic JMS and third-party JMS products, a messaging bridge communicates with source and target destinations using the Java EE Connector Architecture (JCA) resource adapters provided with WebLogic Server.
&lt;br/&gt;&lt;br/&gt;
Untarget the JMS system resource that contains the foreign server, and stop the application containing the message-driven bean (first we just want to check the message forwarding).
&lt;br/&gt;&lt;br/&gt;
To configure a messaging bridge we follow the steps presented &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/wlach/taskhelp/bridge/ConfigureandManageMsgBridge.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. The configuration of the messaging bridge looks as follows:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# target JMS resources
# persistent file store
&amp;lt;file-store&amp;gt;
    &amp;lt;name&amp;gt;teststore&amp;lt;/name&amp;gt;
    &amp;lt;directory&amp;gt;/mnt/hgfs/vmware_temp/file_stores&amp;lt;/directory&amp;gt;
    &amp;lt;target&amp;gt;testserver&amp;lt;/target&amp;gt;
&amp;lt;/file-store&amp;gt;

# jms server
&amp;lt;jms-server&amp;gt;
    &amp;lt;name&amp;gt;testjmsserver&amp;lt;/name&amp;gt;
    &amp;lt;target&amp;gt;testserver&amp;lt;/target&amp;gt;
    &amp;lt;persistent-store&amp;gt;teststore&amp;lt;/persistent-store&amp;gt;
&amp;lt;/jms-server&amp;gt;

# jms system resource
&amp;lt;jms-system-resource&amp;gt;
    &amp;lt;name&amp;gt;testbridgemodule&amp;lt;/name&amp;gt;
    &amp;lt;target&amp;gt;testserver&amp;lt;/target&amp;gt;
    &amp;lt;sub-deployment&amp;gt;
      &amp;lt;name&amp;gt;testsubdeployment&amp;lt;/name&amp;gt;
      &amp;lt;target&amp;gt;testjmsserver&amp;lt;/target&amp;gt;
    &amp;lt;/sub-deployment&amp;gt;
    &amp;lt;descriptor-file-name&amp;gt;jms/testbridgemodule-jms.xml&amp;lt;/descriptor-file-name&amp;gt;
&amp;lt;/jms-system-resource&amp;gt;

# jms resources ( queue and connection factory )
&amp;lt;weblogic-jms ...&amp;gt;
  &amp;lt;connection-factory name=&quot;ConnectionFactory&quot;&amp;gt;
    &amp;lt;default-targeting-enabled&amp;gt;true&amp;lt;/default-targeting-enabled&amp;gt;
    &amp;lt;jndi-name&amp;gt;jms/TestForeignConnectionFactory-0&amp;lt;/jndi-name&amp;gt;
    &amp;lt;client-params&amp;gt;
      &amp;lt;client-id-policy&amp;gt;Restricted&amp;lt;/client-id-policy&amp;gt;
      &amp;lt;subscription-sharing-policy&amp;gt;Exclusive&amp;lt;/subscription-sharing-policy&amp;gt;
      &amp;lt;messages-maximum&amp;gt;10&amp;lt;/messages-maximum&amp;gt;
    &amp;lt;/client-params&amp;gt;
    &amp;lt;transaction-params&amp;gt;
      &amp;lt;xa-connection-factory-enabled&amp;gt;true&amp;lt;/xa-connection-factory-enabled&amp;gt;
    &amp;lt;/transaction-params&amp;gt;
    &amp;lt;security-params&amp;gt;
      &amp;lt;attach-jmsx-user-id&amp;gt;false&amp;lt;/attach-jmsx-user-id&amp;gt;
    &amp;lt;/security-params&amp;gt;
  &amp;lt;/connection-factory&amp;gt;
  &amp;lt;queue name=&quot;Queue&quot;&amp;gt;
    &amp;lt;sub-deployment-name&amp;gt;testsubdeployment&amp;lt;/sub-deployment-name&amp;gt;
    &amp;lt;jndi-name&amp;gt;jms/TestForeignDestination&amp;lt;/jndi-name&amp;gt;
  &amp;lt;/queue&amp;gt;
&amp;lt;/weblogic-jms&amp;gt;

# bridge
# source destination (WildFly)
&amp;lt;jms-bridge-destination&amp;gt;
    &amp;lt;name&amp;gt;SourceDestination&amp;lt;/name&amp;gt;
    &amp;lt;adapter-jndi-name&amp;gt;eis.jms.WLSConnectionFactoryJNDINoTX&amp;lt;/adapter-jndi-name&amp;gt;
    &amp;lt;user-name&amp;gt;employee&amp;lt;/user-name&amp;gt;
    &amp;lt;user-password-encrypted&amp;gt;{AES256}g3eYe06CVfMSxyGpK0IuavG2j3bR2c2+LGy/QEsaaHk=&amp;lt;/user-password-encrypted&amp;gt;
    &amp;lt;connection-factory-jndi-name&amp;gt;jms/RemoteConnectionFactory&amp;lt;/connection-factory-jndi-name&amp;gt;
    &amp;lt;initial-context-factory&amp;gt;org.wildfly.naming.client.WildFlyInitialContextFactory&amp;lt;/initial-context-factory&amp;gt;
    &amp;lt;connection-url&amp;gt;http-remoting://javanode1.ms.com:8080&amp;lt;/connection-url&amp;gt;
    &amp;lt;destination-jndi-name&amp;gt;jms/queue/test&amp;lt;/destination-jndi-name&amp;gt;
    &amp;lt;destination-type&amp;gt;Queue&amp;lt;/destination-type&amp;gt;
&amp;lt;/jms-bridge-destination&amp;gt;

# target destination (WebLogic)
&amp;lt;jms-bridge-destination&amp;gt;
    &amp;lt;name&amp;gt;TargetDestination&amp;lt;/name&amp;gt;
    &amp;lt;adapter-jndi-name&amp;gt;eis.jms.WLSConnectionFactoryJNDINoTX&amp;lt;/adapter-jndi-name&amp;gt;
    &amp;lt;connection-factory-jndi-name&amp;gt;jms/TestForeignConnectionFactory-0&amp;lt;/connection-factory-jndi-name&amp;gt;
    &amp;lt;connection-url&amp;gt;t3://javanode1.ms.com:10080&amp;lt;/connection-url&amp;gt;
    &amp;lt;destination-jndi-name&amp;gt;jms/TestForeignDestination&amp;lt;/destination-jndi-name&amp;gt;
&amp;lt;/jms-bridge-destination&amp;gt;

# messaging bridge
&amp;lt;messaging-bridge&amp;gt;
    &amp;lt;name&amp;gt;Bridge&amp;lt;/name&amp;gt;
    &amp;lt;target&amp;gt;testserver&amp;lt;/target&amp;gt;
    &amp;lt;source-destination&amp;gt;SourceDestination&amp;lt;/source-destination&amp;gt;
    &amp;lt;target-destination&amp;gt;TargetDestination&amp;lt;/target-destination&amp;gt;
    &amp;lt;selector&amp;gt;&amp;lt;/selector&amp;gt;
    &amp;lt;quality-of-service&amp;gt;Atmost-once&amp;lt;/quality-of-service&amp;gt;
    &amp;lt;started&amp;gt;true&amp;lt;/started&amp;gt;
&amp;lt;/messaging-bridge&amp;gt;
&lt;/pre&gt;

Check the WildFly and WebLogic console to see if the set-up is working

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDjnIPlXlwZ2KDUQkHugP3pxdYuiZiouVbvaVO8b37GD9uSlE8xP8dqhVzU8_MBslaGVM_NV1guSU3dtN0JCyMPpwTLrUdsz4UMkNJlZ8lIOM7PZv10oGGOkIyHOsZmM-XgAhnm7_Ep4BW/s640/wildfly_message_bridge_connected.png&quot; alt=&quot;wildfly_message_bridge_connected&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOnNZvXKmSC79gcS7G3M3mxd27PyGM6dUM3yIDtlGQiiNjn65CPLqRbWtd4zyMkn27UuGmv-ZNPbGABxhsx2c8C9EueRBP-iKLNLTqLvqjRcirzrlV1MZ8pIRXpg5_HUSmMtL6vezTDq1J/s640/weblogic_message_bridge_connected.png&quot; alt=&quot;weblogic_message_bridge_connected&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

Send a message

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-xyqhrSORrG8KuMo6z0slOCfPF8mXNGZeJDTsXqmGkjTbDiV6CVkq8YIONDA4KmXFcXHdEB71_LTKqrNY2yhEzJldfZuwK5qyApgj_kzJnl8d16RCZb9-gaLfir04aSXlMKg5f19EIGz2/s640/weblogic_send_message.png&quot; alt=&quot;weblogic_send_message&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

Start the application containing the message-driven bean to consume the message, and check the logging (for no particular reason).

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# $WILDFLY_HOME/standalone/log/server.log
2020-05-21 09:54:48,155 INFO  [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0212: Resuming server
2020-05-21 09:54:48,158 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://192.168.101.110:9990/management
2020-05-21 09:54:48,158 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://192.168.101.110:9990
2020-05-21 09:54:48,158 INFO  [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 19.1.0.Final (WildFly Core 11.1.1.Final) started in 5487ms - Started 359 of 626 services (393 services are lazy, passive or on-demand)
2020-05-21 09:55:01,243 INFO  [org.wildfly.naming] (default task-1) WildFly Naming version 1.0.12.Final

# $DOMAIN_HOME/servers/testserver/logs/testserver.out
&amp;lt;May 21, 2020, 9:58:32,127 AM Central European Summer Time&amp;gt; &amp;lt;Notice&amp;gt; &amp;lt;WebLogicServer&amp;gt; &amp;lt;BEA-000330&amp;gt; &amp;lt;Started the WebLogic Server Managed Server &quot;testserver&quot; for domain &quot;tryout_domain&quot; running in production mode.&amp;gt; 
&amp;lt;May 21, 2020, 9:58:32,133 AM Central European Summer Time&amp;gt; &amp;lt;Notice&amp;gt; &amp;lt;WebLogicServer&amp;gt; &amp;lt;BEA-000360&amp;gt; &amp;lt;The server started in RUNNING mode.&amp;gt; 
&amp;lt;May 21, 2020, 9:58:32,143 AM Central European Summer Time&amp;gt; &amp;lt;Notice&amp;gt; &amp;lt;WebLogicServer&amp;gt; &amp;lt;BEA-000365&amp;gt; &amp;lt;Server state changed to RUNNING.&amp;gt; 
May 21, 2020 9:58:32 AM org.wildfly.naming.client.Version &amp;lt;clinit&amp;gt;
INFO: WildFly Naming version 1.0.12.Final
May 21, 2020 9:58:32 AM org.wildfly.security.Version &amp;lt;clinit&amp;gt;
INFO: ELY00001: WildFly Elytron version 1.11.4.Final
May 21, 2020 9:58:33 AM org.xnio.Xnio &amp;lt;clinit&amp;gt;
INFO: XNIO version 3.7.7.Final
May 21, 2020 9:58:33 AM org.xnio.nio.NioXnio &amp;lt;clinit&amp;gt;
INFO: XNIO NIO Implementation Version 3.7.7.Final
May 21, 2020 9:58:33 AM org.jboss.threads.Version &amp;lt;clinit&amp;gt;
INFO: JBoss Threads version 2.3.3.Final
May 21, 2020 9:58:33 AM org.jboss.remoting3.EndpointImpl &amp;lt;clinit&amp;gt;
INFO: JBoss Remoting version 5.0.17.Final
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by javassist.util.proxy.SecurityActions (file:/u01/app/oracle/weblogic14.1.1/installation/oracle_common/modules/org.javassist.javassist.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of javassist.util.proxy.SecurityActions
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
SLF4J: Failed to load class &quot;org.slf4j.impl.StaticLoggerBinder&quot;.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
RECEIVED TEXT MESSAGE Send some useful message
RECEIVED TEXT MESSAGE Send some other useful message
&lt;/pre&gt;

&lt;h2&gt;References&lt;/h2&gt;
[1] &lt;a href=&quot;https://docs.wildfly.org/19.1/&quot; target=&quot;_blank&quot;&gt;WildFly Documentation&lt;/a&gt;.&lt;/br&gt;
[2] &lt;a href=&quot;https://docs.oracle.com/en/middleware/standalone/weblogic-server/14.1.1.0/books.html&quot; target=&quot;_blank&quot;&gt;WebLogic Documentation&lt;/a&gt;.&lt;/br&gt;
[3] &lt;a href=&quot;https://javaee.github.io/&quot; target=&quot;_blank&quot;&gt;Java EE Resources&lt;/a&gt;.&lt;/br&gt;
[4] &lt;a href=&quot;https://javaee.github.io/javaee-spec/javadocs/&quot; target=&quot;_blank&quot;&gt;JavaEE 8 Specification APIs&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/6561247541776534724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/6561247541776534724'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/2020/05/from-wildfly-to-weblogic.html' title='From WildFly to WebLogic'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6M1bse_OHrt_q7Go3kCkBj2l-dKNU7dOQZH5eTyAzykHCbQqcM5zJkONUPmYsW7ob1REjCthE6IT1DxOYsYoC4lr7uXZT354ybBBQWZRo8RVp5UtLwGPuyTmG4oAPr1FXJzcJtrfBab8j/s72-c/wildfly_console_initial_runtime.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-4896087204075281708.post-1254510568171298233</id><published>2019-05-20T13:47:00.000+02:00</published><updated>2019-05-21T10:18:23.821+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Automation"/><category scheme="http://www.blogger.com/atom/ns#" term="Database"/><title type='text'>Ansibling the Oracle Database</title><content type='html'>In this post, we give an example of how to set-up an Oracle Database using Ansible.

&lt;h2&gt;Create Ansible Roles&lt;/h2&gt;
Playbooks are Ansible&#39;s configuration, deployment, and orchestration language. While it is possible to write a playbook in one very large file, eventually we will want to reuse files and start to organize things. &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html&quot; target=&quot;_blank&quot;&gt;Roles&lt;/a&gt; in Ansible build on the idea of include files and combine them to form clean, reusable abstractions - they allow us to focus more on the big picture and only dive down into the details when needed. Ansible offers a number of &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/modules.html&quot; target=&quot;_blank&quot;&gt;modules&lt;/a&gt; which are described &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/modules_by_category.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. If the need arises, we can also &lt;a href=&quot;https://docs.ansible.com/ansible/latest/dev_guide/developing_modules.html&quot; target=&quot;_blank&quot;&gt;develop&lt;/a&gt; our own modules.
&lt;br/&gt;&lt;br/&gt;
In order to install and configure an Oracle Database, we create the following roles

&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;oracle_database_vars&lt;/code&gt; - defines the defaults for the used variables.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;oracle_database_host&lt;/code&gt; - configures the Linux host, i.e., configures SELinux, configures the firewall, installs (necessary) packages, creates a group and a user, modifies kernel parameters (&lt;code&gt;/etc/sysctl.conf&lt;/code&gt;), and sets limits on the system resources that can be obtained in a user-session (&lt;code&gt;/etc/security/limits.conf&lt;/code&gt;).&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;oracle_database_install&lt;/code&gt; - installs the Oracle Database software.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;oracle_database_config&lt;/code&gt; - creates a listener and database.&lt;/li&gt;
&lt;/ul&gt;

The directory structure for the roles looks as follows

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
/etc/ansible
    /inventories
        /test
            /group_vars
                database_hosts.yml
            hosts.cfg
    /roles
        /oracle_database_config
            /files
                /oracle/lifecycle
                    start_database.sh
                    stop_database.sh
            /tasks
                main.yml
            /templates
                database_service.j2
                dbca_rsp_12.2.0.j2
                dbca_rsp_19.0.0.j2
                netca_rsp_12.2.0.j2
                netca_rsp_19.0.0.j2
        /oracle_database_host
            /handlers
                main.yml
            /tasks
                main.yml
            /templates
                limits_conf.j2
                sysctl_conf.j2
        /oracle_database_install
            /tasks
                main.yml
            /templates
                db_install_rsp_12.2.0.j2
                db_install_rsp_19.0.0.j2
        /oracle_database_vars
            /defaults
                main.yml
    oracle_database.yml
/mnt/hgfs/vmware_temp
    /database_12.2.0
        /install
        /response
        /rpm
        /sshsetup
        /stage
        runInstaller
        welcome.html
    /database_19.0.0
        V982063-01.zip
&lt;/pre&gt;

&lt;h3&gt;Configure (Linux) host&lt;/h3&gt;

To configure the (Linux) host, we use the following:

&lt;ul&gt;
    &lt;li&gt;Used variables
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##### User and group settings #####
# User and Group under which the software needs to be installed
oracle_install_user: &quot;oracle&quot;
oracle_install_password: &quot;set_in_environment_specific_group_vars_file&quot;
oracle_install_group: &quot;oinstall&quot;
oracle_install_group_id: 54321


##### OS settings #####
# number of open files
number_of_open_files: 65536

# number of processes
number_of_processes: 16384

# stack size
stack_size: 10240

# memory that will be used for huge pages
memory_to_reserve_in_mega_bytes: 256

number_of_huge_pages: &quot;{{ memory_to_reserve_in_mega_bytes // 2 }}&quot;
mem_lock: &quot;{{ 2048 * (memory_to_reserve_in_mega_bytes // 2) }}&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;On Oracle Linux it is possible to configure the host using the &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/automatically-configuring-oracle-linux-with-oracle-preinstallation-rpm.html#GUID-22846194-58EF-4552-AAC3-6F6D0A1DF794&quot; target=&quot;_blank&quot;&gt;preinstallation RPM&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Install the &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/supported-red-hat-enterprise-linux-7-distributions-for-x86-64.html#GUID-2E11B561-6587-4789-A583-2E33D705E498&quot; target=&quot;_blank&quot;&gt;required packages&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Create the &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/configuring-users-groups-and-environments-for-oracle-grid-infrastructure-and-oracle-database.html#GUID-B65E6113-D056-4DD9-940F-DFF493E413D5&quot; target=&quot;_blank&quot;&gt;group and user&lt;/a&gt; under which the Oracle Database will be installed and configured. Note that the password option of the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/user_module.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;user module&lt;/code&gt;&lt;/a&gt; must be encrypted. To create an encrypted password, we can use &lt;code&gt;python -c &#39;import crypt; print crypt.crypt(&quot;user_password&quot;, &quot;$id$salt&quot;)&#39;&lt;/code&gt;, in which &lt;code&gt;id&lt;/code&gt; can be &lt;code&gt;1&lt;/code&gt; (MD5), &lt;code&gt;5&lt;/code&gt; (SHA-256 since glibc 2.7), or &lt;code&gt;6&lt;/code&gt; (SHA-512 since glibc 2.7), and &lt;code&gt;salt&lt;/code&gt; stands for the up to 16 characters following &lt;code&gt;$id$&lt;/code&gt;, more information can be found in the &lt;a href=&quot;http://man7.org/linux/man-pages/man3/crypt.3.html&quot; target=&quot;_blank&quot;&gt;crypt(3) Linux manual page&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Modify &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/configuring-kernel-parameters-for-linux.html#GUID-6127884D-FB27-45FA-9498-B2540632CBD5&quot; target=&quot;_blank&quot;&gt;kernel parameters&lt;/a&gt;
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled.  See sysctl(8) and
# sysctl.conf(5) for more details.

# Controls IP packet forwarding
net.ipv4.ip_forward = 0

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1

# Controls the default maxmimum size of a mesage queue
kernel.msgmnb = 65536

# Controls the maximum size of a message, in bytes
kernel.msgmax = 65536

# Controls the maximum shared segment size, in bytes
kernel.shmmax = 68719476736

# Controls the maximum number of shared memory segments, in pages
kernel.shmall = 4294967296

# semaphores
kernel.sem = 250 32000 100 128
# shared memory
kernel.shmmni = 4096

# file handles
fs.file-max = 6815744

# asynchronous i/o
fs.aio-max-nr = 1048576

# disable ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

# default range of IP port numbers that are allowed for TCP and UDP traffic
net.ipv4.ip_local_port_range = 9000 65535

# increase TCP max buffer size (depending on the type of NIC and the round-trip time these values can be changed)
# Maximum TCP Receive Window
net.core.rmem_max = 8388608
net.core.rmem_default = 8388608
# Maximum TCP Send Window
net.core.wmem_max = 8388608
net.core.wmem_default = 8388608
#  memory reserved for TCP receive buffers (vector of 3 integers: [min, default, max])
net.ipv4.tcp_rmem = 4096 87380 8388608
# memory reserved for TCP send buffers (vector of 3 integers: [min, default, max])
net.ipv4.tcp_wmem = 4096 87380 8388608

# increase the length of the processor input queue
net.core.netdev_max_backlog = 30000
# maximum amount of memory buffers (could be set equal to net.core.rmem_max and net.core.wmem_max)
net.core.optmem_max = 20480
# socket of the listen backlog
net.core.somaxconn = 1024

# tcp selective acknowledgements (disable them on high-speed networks)
net.ipv4.tcp_sack = 1
net.ipv4.tcp_dsack = 1
# Timestamps add 12 bytes to the TCP header
net.ipv4.tcp_timestamps = 1
# Support for large TCP Windows - Needs to be set to 1 if the Max TCP Window is over 65535
net.ipv4.tcp_window_scaling = 1
# The interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe
net.ipv4.tcp_keepalive_time = 1800
# The interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime
net.ipv4.tcp_keepalive_intvl = 30
# The number of unacknowledged probes to send before considering the connection dead and notifying the application layer
net.ipv4.tcp_keepalive_probes = 5
# The time that must elapse before TCP/IP can release a closed connection and reuse its resources.
net.ipv4.tcp_fin_timeout = 30
# Size of the backlog connections queue.
net.ipv4.tcp_max_syn_backlog = 4096
# The tcp_tw_reuse setting is particularly useful in environments where numerous short connections are open and left in TIME_WAIT state, such as web servers.
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

# The percentage of how aggressively memory pages are swapped to disk
vm.swappiness = 0
# The percentage of main memory the pdflush daemon should write data out to the disk.
vm.dirty_background_ratio = 25
# The percentage of main memory the actual disk writes will take place.
vm.dirty_ratio = 20

# set the number of huge pages based on the Hugepagesize, i.e., 2048kB
#vm.nr_hugepages = {{ number_of_huge_pages }}
 
# give permission to the group that runs the process to access the shared memory segment
#vm.hugetlb_shm_group = {{ oracle_install_group_id }}
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Modify the &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/checking-resource-limits-for-oracle-software-installation-users.html#GUID-293874BD-8069-470F-BEBF-A77C06618D5A&quot; target=&quot;_blank&quot;&gt;limits&lt;/a&gt; on the system resources
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
#&amp;lt;domain&amp;gt;        &amp;lt;type&amp;gt;  &amp;lt;item&amp;gt;  &amp;lt;value&amp;gt;
#
#Where:
#&amp;lt;domain&amp;gt; can be:
#        - a user name
#        - a group name, with @group syntax
#        - the wildcard *, for default entry
#        - the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#
#&amp;lt;type&amp;gt; can have the two values:
#        - &quot;soft&quot; for enforcing the soft limits
#        - &quot;hard&quot; for enforcing hard limits
#
#&amp;lt;item&amp;gt; can be one of the following:
#        - core - limits the core file size (KB)
#        - data - max data size (KB)
#        - fsize - maximum filesize (KB)
#        - memlock - max locked-in-memory address space (KB)
#        - nofile - max number of open file descriptors
#        - rss - max resident set size (KB)
#        - stack - max stack size (KB)
#        - cpu - max CPU time (MIN)
#        - nproc - max number of processes
#        - as - address space limit (KB)
#        - maxlogins - max number of logins for this user
#        - maxsyslogins - max number of logins on the system
#        - priority - the priority to run user process with
#        - locks - max number of file locks the user can hold
#        - sigpending - max number of pending signals
#        - msgqueue - max memory used by POSIX message queues (bytes)
#        - nice - max nice priority allowed to raise to values: [-20, 19]
#        - rtprio - max realtime priority
#
#&amp;lt;domain&amp;gt;      &amp;lt;type&amp;gt;  &amp;lt;item&amp;gt;         &amp;lt;value&amp;gt;
#

#*               soft    core            0
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#@student        -       maxlogins       4


# open file descriptors
@{{ oracle_install_group }} soft nofile {{ number_of_open_files }}
@{{ oracle_install_group }} hard nofile {{ number_of_open_files }}

#max number of processes
@{{ oracle_install_group }} soft nproc {{ number_of_processes }}
@{{ oracle_install_group }} hard nproc {{ number_of_processes }}

#max stack size (KB)
@{{ oracle_install_group }} soft stack {{ stack_size }}
@{{ oracle_install_group }} hard stack {{ stack_size }}

# memlock - maximum locked in-memory address space (kB)
#@{{ oracle_install_group }} soft memlock {{ mem_lock }}
#@{{ oracle_install_group }} hard memlock {{ mem_lock }}


# End of file
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure the Firewall. To this end, we use the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/firewalld_module.html&quot; target=&quot;_blank&quot;&gt;firewalld&lt;/a&gt; module, and add the listen port, in this case, 1521.&lt;/li&gt;
    &lt;li&gt;Configure SELinux, to this end, we can use the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/selinux_module.html&quot; target=&quot;_blank&quot;&gt;selinux&lt;/a&gt; (and the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/seboolean_module.html&quot; target=&quot;_blank&quot;&gt;seboolean&lt;/a&gt;, &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/sefcontext_module.html#sefcontext-module&quot; target=&quot;_blank&quot;&gt;sefcontext&lt;/a&gt;, &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/seport_module.html#seport-module&quot; target=&quot;_blank&quot;&gt;seport&lt;/a&gt;) module(s). Note that SELinux is configured by default for the Oracle Database, we can check this by using &lt;code&gt;sesearch --allow | grep -i oracle&lt;/code&gt; or a bit more finegrained, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@datanode ~]# sesearch --allow --source=httpd_t | grep -i oracle
   allow httpd_t oracle_client_packet_t : packet recv ;
   allow httpd_t oracle_client_packet_t : packet send ;
   allow httpd_t oracle_port_t : tcp_socket name_connect ;
&lt;/pre&gt;
    also check which ports are accepted
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@datanode ~]# semanage port -l | grep oracle_port_t
oracle_port_t                  tcp      1521, 2483, 2484
oracle_port_t                  udp      1521, 2483, 2484
&lt;/pre&gt;
    and check file context mappings
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@datanode ~]# semanage fcontext -l | grep etc_t
/etc/.*                                            all files          system_u:object_r:etc_t:s0
...

[root@datanode ~]# ll /etc/ora*
-rw-r--r--. 1 root   root      56 May  9 13:33 /etc/oraInst.loc
-rw-rw-r--. 1 oracle oinstall 786 May  9 13:34 /etc/oratab
&lt;/pre&gt;
    when installing and configuring it is always smart to leave SELinux in permissive mode and check the audit logs (&lt;code&gt;/var/log/audit/audit.log&lt;/code&gt;). SELinux troubleshooting is explained in the article: &lt;a href=&quot;https://access.redhat.com/articles/2191331&quot; target=&quot;_blank&quot;&gt;Basic SELinux Troubleshooting in CLI&lt;/a&gt;.
    &lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following 

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/ansible/roles/oracle_database_host/tasks/main.yml

#- name: install oracle database server preinstall
#  yum:
#    name: http://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm
#    name: http://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-server-12cR2-preinstall-1.0-4.el7.x86_64.rpm
#    state: present

- name: install necessary packages
  yum:
    name={{ item }}
    state=present
  loop:
    - binutils
    - compat-libcap1
    - compat-libstdc++-33
    - dos2unix
    - gcc
    - gcc-c++
    - glibc
    - glibc-devel
    - kmod
    - ksh
    - libaio
    - libaio-devel
    - libgcc
    - libstdc++
    - libstdc++-devel
    - libX11
    - libXau
    - libxcb
    - libXext
    - libXi
    - libXrender
    - libXtst
    - lsof
    - ksh
    - make
    - net-tools
    - perl
    - smartmontools
    - strace
    - sysstat
    - unzip

- name: create group
  group:
    name={{ oracle_install_group }}
    gid={{ oracle_install_group_id }}
    state=present
    
- name: create user
  user:
    name={{ oracle_install_user }}
    state=present
    createhome=yes
    group={{ oracle_install_group_id }}
    password={{ oracle_install_password }}

# kernel parameters
- name: set sysctl /etc/sysctl.d/10-default_sysctl.conf
  template: 
    src=roles/oracle_database_host/templates/sysctl_conf.j2 
    dest=/etc/sysctl.d/10-default_sysctl.conf
    owner=root
    group=root
    mode=0644
  register: sysctl_changed
  
- name: run sysctl -p
  command: /bin/sh -c &quot;/sbin/sysctl -p&quot;
  when: sysctl_changed is changed
    
# linux resource limits
- name: set /etc/security/limits.conf
  template: 
    src=roles/oracle_database_host/templates/limits_conf.j2 
    dest=/etc/security/limits.conf
    owner=root
    group=root
    mode=0644
    
# configure firewall
- name: check if iptables exists
  stat: path=/usr/lib/systemd/system/iptables.service
  register: iptables_status
    
- name: disable iptables
  service:
    name=iptables
    state=stopped
    enabled=no
  when: iptables_status.stat.exists
  
- name: check if firewalld exists
  stat: path=/usr/lib/systemd/system/firewalld.service
  register: firewalld_status

- name: enable firewalld
  systemd:
    name: firewalld
    state: started
    enabled: yes
  when: firewalld_status.stat.exists
  register: firewall_started

- name: activate public zone by adding interface to public zone
  firewalld:
    zone: public
    interface: &quot;{{ ansible_default_ipv4.interface }}&quot;
    state: enabled
    permanent: yes
    immediate: yes
  when: firewall_started is success
    
- name: remove dhcpv6-client service from zone public
  firewalld:
    zone: public
    service: dhcpv6-client
    state: disabled
    permanent: yes
    immediate: yes
  when: firewall_started is success

- name: add database listen port to zone public
  firewalld:
    zone: public
    port: 1521/tcp
    state: enabled
    permanent: yes
    immediate: yes
  when: firewall_started is success

# configure selinux
- name: enable selinux
  selinux:
    policy=targeted
    state=permissive
  notify:
    - restart server
    - wait for server to restart


# /etc/ansible/roles/database_host/handlers/main.yml

- name: restart server
  shell: sleep 2 &amp;&amp; shutdown -r now &quot;Ansible updates triggered&quot;
  async: 1
  poll: 0
  ignore_errors: True
  
- name: wait for server to restart
  local_action: wait_for
    host={{ inventory_hostname }}
    port=22
    state=started
    delay=10
    timeout=300
  become: false
&lt;/pre&gt;

&lt;h3&gt;Installation&lt;/h3&gt;
To install Oracle Database software, we use the following:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##### database/grid version specification #####
software_version: &quot;19.0.0&quot;
# Directory where the software to be installed is located
software_directory: &quot;/mnt/hgfs/vmware_temp/database_{{ software_version }}&quot;

# when software version is 18 or higher choose image or rpm based install
image_based_software_installation: &quot;true&quot;
image_name: &quot;V982063-01.zip&quot;

rpm_based_software_installation: &quot;false&quot;
rpm_path: &quot;oracle-database-ee-19c-1.0-1.x86_64.rpm&quot;

##### database directories #####
# The scripts create files that are placed in this directory
temporary_directory: &quot;/u01/repsonse_files_{{ software_version }}&quot;
# root directory
root_directory: &quot;/u01&quot;
# Scripts directory
scripts_directory: &quot;{{ root_directory }}/app/scripts&quot;
# Location of the Oracle inventory
oracle_inventory_home: &quot;{{ root_directory }}/app/oraInventory&quot;
# Base directory
base_directory: &quot;{{ root_directory }}/app/oracle&quot;
# Oracle home
oracle_home: &quot;{{ base_directory }}/product/{{ software_version }}/dbhome&quot;

##### install options #####
oracle_hostname: &quot;set_in_environment_specific_group_vars_file&quot;
oracle_install_option: &quot;INSTALL_DB_SWONLY&quot;
oracle_installation_edition: &quot;EE&quot;
oracle_execute_root_script: &quot;false&quot;

##### files #####
run_installer_12c: &quot;{{ software_directory }}/runInstaller&quot;
run_installer: &quot;{{ oracle_home }}/runInstaller&quot;
net_creation_assistent: &quot;{{ oracle_home }}/bin/netca&quot;
database_creation_assistent: &quot;{{ oracle_home }}/bin/dbca&quot;

silent_install_file: &quot;{{ temporary_directory }}/silent-install.rsp&quot;
silent_listener_config_file: &quot;{{ temporary_directory }}/silent-listener-config.rsp&quot;
silent_database_config_file: &quot;{{ temporary_directory }}/silent-database-config.rsp&quot;

root_sh: &quot;{{ oracle_home }}/root.sh&quot;
orainst_root_sh: &quot;{{ oracle_inventory_home }}/orainstRoot.sh&quot;

orainst_loc: &quot;/etc/oraInst.loc&quot;
oratab: &quot;/etc/oratab&quot;

listener_ora: &quot;{{ oracle_home }}/network/admin/listener.ora&quot;
sp_file: &quot;{{ oracle_home }}/dbs/spfile{{ database_sid }}.ora&quot;
database_service: &quot;{{ scripts_directory }}/database_service&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Create &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/optimal-flexible-architecture.html#GUID-34434C8B-EBEE-497A-BB92-26B43561B6B1&quot; target=&quot;_blank&quot;&gt;directory&lt;/a&gt; structure and set the permissions.&lt;/li&gt;
    &lt;li&gt;Based on the &lt;code&gt;image_based_software_installation&lt;/code&gt; and &lt;code&gt;rpm_based_software_installation&lt;/code&gt; parameters, the software is installed by using either an &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/about-image-based-database-installation.html#GUID-0315AA15-1C03-4E96-B801-3A8E5F5D4B39&quot; target=&quot;_blank&quot;&gt;image&lt;/a&gt; or an &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/installing-oracle-database-using-rpm-packages.html#GUID-5AF74AC1-510E-4EB0-9BCA-B096C42C6A76&quot; target=&quot;_blank&quot;&gt;RPM&lt;/a&gt; (or when both are &lt;code&gt;false&lt;/code&gt; the old fashioned &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/12.2/ladbi/index.html&quot; target=&quot;_blank&quot;&gt;12c&lt;/a&gt; way). Note that 
&lt;blockquote cite=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/about-image-based-database-installation.html#GUID-0315AA15-1C03-4E96-B801-3A8E5F5D4B39&quot;&gt;
Starting with Oracle Database 18c, installation and configuration of Oracle Database software is simplified with image-based installation. To install Oracle Database, create the new Oracle home, extract the image file into the newly-created Oracle home, and run the setup wizard to register the Oracle Database product. Using image-based installation, you can install and upgrade Oracle Database for single-instance and cluster configurations. This installation feature streamlines the installation process and supports automation of large-scale custom deployments. You can also use this installation method for deployment of customized images, after you patch the base-release software with the necessary Release Updates (Updates) or Release Update Revisions (Revisions).
&lt;/blockquote&gt;

&lt;blockquote cite=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/about-rpm-based-oracle-database-installation.html#GUID-C2B4A806-E1E2-4B77-971B-628CFF18A488&quot;&gt;
Starting with Oracle Database 18c, you can install a single-instance Oracle Database or an Oracle Database Instant Client software using RPM packages. An RPM-based installation performs preinstallation checks, extracts the database software, reassigns ownership of the extracted software to the preconfigured user and groups, maintains the Oracle inventory, and executes all root operations required to configure the Oracle Database software for a single-instance Oracle Database creation and configuration. The RPM–based installation process detects when the minimum requirements for an installation are not met and prompts you to finish these minimum preinstallation requirements. An RPM-based installation performs a software-only Oracle Database installation and creates an Oracle home. After the Oracle home is created, you can then use Oracle Database Configuration Assistant (Oracle DBCA) to create an Oracle Database.
&lt;/blockquote&gt;
    &lt;/li&gt;
    &lt;li&gt;Create &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/cwlin/installing-and-configuring-oracle-database-using-response-files.html#GUID-D53355E9-E901-4224-9A2A-B882070EDDF7&quot; target=&quot;_blank&quot;&gt;response file&lt;/a&gt;, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
####################################################################
## Copyright(c) Oracle Corporation 1998,2019. All rights reserved.##
##                                                                ##
## Specify values for the variables listed below to customize     ##
## your installation.                                             ##
##                                                                ##
## Each variable is associated with a comment. The comment        ##
## can help to populate the variables with the appropriate        ##
## values.                                                        ##
##                                                                ##
## IMPORTANT NOTE: This file contains plain text passwords and    ##
## should be secured to have read permission only by oracle user  ##
## or db administrator who owns this installation.                ##
##                                                                ##
####################################################################


#------------------------------------------------------------------------------
# Do not change the following system generated value. 
#------------------------------------------------------------------------------
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v19.0.0

#-------------------------------------------------------------------------------
# Specify the installation option.
# It can be one of the following:
#   - INSTALL_DB_SWONLY
#   - INSTALL_DB_AND_CONFIG
#-------------------------------------------------------------------------------
oracle.install.option={{ oracle_install_option }}

#-------------------------------------------------------------------------------
# Specify the Unix group to be set for the inventory directory.  
#-------------------------------------------------------------------------------
UNIX_GROUP_NAME={{ oracle_install_group }}

#-------------------------------------------------------------------------------
# Specify the location which holds the inventory files.
# This is an optional parameter if installing on
# Windows based Operating System.
#-------------------------------------------------------------------------------
INVENTORY_LOCATION={{ oracle_inventory_home }}
#-------------------------------------------------------------------------------
# Specify the complete path of the Oracle Home. 
#-------------------------------------------------------------------------------
ORACLE_HOME={{ oracle_home }}

#-------------------------------------------------------------------------------
# Specify the complete path of the Oracle Base. 
#-------------------------------------------------------------------------------
ORACLE_BASE={{ base_directory }}
...
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/running-oracle-universal-installer-using-a-response-file.html#GUID-C8B6ED11-4486-45E0-8365-E1A9695FB27C&quot; target=&quot;_blank&quot;&gt;runInstaller&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;orainstRoot.sh&lt;/code&gt; and &lt;code&gt;root.sh&lt;/code&gt; (see &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/running-oracle-universal-installer-using-a-response-file.html#GUID-C8B6ED11-4486-45E0-8365-E1A9695FB27C&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information). Note that
&lt;blockquote cite=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/changes-in-this-release-for-oracle-database.html#GUID-1B11526F-88FE-460B-96C1-8D59033A2C52&quot;&gt;
Starting with Oracle Database 19c, the database installer, or setup wizard, provides options to set up permissions to run the root configuration scripts automatically, as required, during a database installation. You continue to have the option to run the root configuration scripts manually. Setting up permissions for root configuration scripts to run without user intervention can simplify database installation and help avoid inadvertent permission errors.
&lt;/blockquote&gt;
    As we are using Ansible the root script execution mode
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
################################################################################
#                                                                              #
#                      Root script execution configuration                     #
#                                                                              #
################################################################################

#-------------------------------------------------------------------------------------------------------
# Specify the root script execution mode.
#
#   - true  : To execute the root script automatically by using the appropriate configuration methods.
#   - false : To execute the root script manually.
#
# If this option is selected, password should be specified on the console.
#-------------------------------------------------------------------------------------------------------
oracle.install.db.rootconfig.executeRootScript={{ oracle_execute_root_script }}

#--------------------------------------------------------------------------------------
# Specify the configuration method to be used for automatic root script execution.
#
# Following are the possible choices:
#   - ROOT
#   - SUDO
#--------------------------------------------------------------------------------------
oracle.install.db.rootconfig.configMethod=
#--------------------------------------------------------------------------------------
# Specify the absolute path of the sudo program.
#
# Applicable only when SUDO configuration method was chosen.
#--------------------------------------------------------------------------------------
oracle.install.db.rootconfig.sudoPath=

#--------------------------------------------------------------------------------------
# Specify the name of the user who is in the sudoers list. 
# Applicable only when SUDO configuration method was chosen.
# Note:For Single Instance database installations,the sudo user name must be the username of the user installing the database.
#--------------------------------------------------------------------------------------
oracle.install.db.rootconfig.sudoUserName=
&lt;/pre&gt;
    is set to &lt;code&gt;false&lt;/code&gt;.
    &lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following 

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/ansible/roles/oracle_database_install/tasks/main.yml

- name: create root directory
  file:
    path: &quot;{{ root_directory }}&quot;
    state: directory
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0755
    
- name: create directories
  file:
    path: &quot;{{ item }}&quot;
    state: directory
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0755
  when: not( (item is undefined) or (item is none) or (item | trim==&#39;&#39;) )
  with_items:
    - &quot;{{ temporary_directory }}&quot;
    - &quot;{{ scripts_directory }}&quot;
    - &quot;{{ oracle_home }}&quot;

- name: image based software installation
  unarchive:
    copy: no
    creates: &quot;{{ oracle_home }}/runInstaller&quot;
    src: &quot;{{ software_directory }}/{{ image_name }}&quot;
    dest: &quot;{{ oracle_home }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
  when: image_based_software_installation
    
- name: rpm based software installation
  yum:
    name: &quot;{{ software_directory }}/{{ rpm_name }}&quot;
    state: present
  when: rpm_based_software_installation
    
- name: create silent install file
  template:
    src: roles/oracle_database_install/templates/db_install_rsp_{{ software_version }}.j2 
    dest: &quot;{{ silent_install_file }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0644
    
- name: check if orainstroot exists
  stat: path=&quot;{{ orainst_root_sh }}&quot;
  register: orainstroot_status
    
- name: 12c oracle database install
  become: true
  become_user: &quot;{{ oracle_install_user }}&quot;
  become_method: sudo
  command: /bin/sh -c &quot;{{ software_directory }}/runInstaller -silent -ignorePrereq -waitforcompletion -responseFile {{ silent_install_file }}&quot;
  when: not orainstroot_status.stat.exists and not image_based_software_installation and not rpm_based_software_installation
  ignore_errors: True
  register: oracle_database_installed
  
- name: debug 12c oracle database install
  debug:
    var=oracle_database_installed
  when: oracle_database_installed is changed

- name: install oracle database
  become: true
  become_user: &quot;{{ oracle_install_user }}&quot;
  become_method: sudo
  command: /bin/sh -c &quot;{{ oracle_home }}/runInstaller -silent -ignorePrereq -waitforcompletion -responseFile {{ silent_install_file }}&quot;
  when: not orainstroot_status.stat.exists and (image_based_software_installation or rpm_based_software_installation)
  ignore_errors: True
  register: oracle_database_installed
  
- name: debug install oracle database
  debug:
    var=oracle_database_installed
  when: oracle_database_installed is changed

- name: run orainstRoot.sh
  command: /bin/sh &quot;{{ orainst_root_sh }}&quot;
    creates=&quot;{{ orainst_loc }}&quot;

- name: run root.sh
  command: /bin/sh &quot;{{ root_sh }}&quot;
    creates=&quot;{{ oratab }}&quot;
    
- name: delete temporary setup files that contain usernames and passwords
  file:
    path: &quot;{{ item }}&quot;
    state: absent
  loop:
    - &quot;{{ silent_install_file }}&quot;
&lt;/pre&gt;

&lt;h3&gt;Configuration&lt;/h3&gt;

To configure the Oracle Database, we use the following:

&lt;ul&gt;
    &lt;li&gt;Used variables
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##### configuration options #####
listener_name: &quot;LISTENER&quot;
listener_protocol: &quot;TCP;1521&quot;

global_database_name: &quot;orcl.ms.com&quot;
database_sid: &quot;orcl&quot;
database_config_type: &quot;SI&quot;
policy_managed: &quot;FALSE&quot;
create_server_pool: &quot;FALSE&quot;
create_as_container_database: &quot;FALSE&quot;
template_name: &quot;General_Purpose.dbc&quot;
sys_password: &quot;set_in_environment_specific_group_vars_file&quot;
system_password: &quot;set_in_environment_specific_group_vars_file&quot;
em_configuration: &quot;NONE&quot;
data_vault_configuration: &quot;FALSE&quot;
oracle_label_security_configuration: &quot;FALSE&quot;
storage_type: &quot;FS&quot;
character_set: &quot;AL32UTF8&quot;
register_with_dir_service: &quot;FALSE&quot;
sample_schema: &quot;FALSE&quot;
memory_percentage: 40
database_type: &quot;OLTP&quot;
automatic_memory_management: &quot;TRUE&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Create a response file for the net configuration assistent, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
###################################################################### 
## Copyright(c) 1998, 2018 Oracle Corporation. All rights reserved. ## 
##                                                                  ## 
## Specify values for the variables listed below to customize your  ## 
## installation.                                                    ## 
##                                                                  ## 
## Each variable is associated with a comment. The comment          ## 
## identifies the variable type.                                    ## 
##                                                                  ## 
## Please specify the values in the following format:               ## 
##                                                                  ## 
##         Type         Example                                     ## 
##         String       &quot;Sample Value&quot;                              ## 
##         Boolean      True or False                               ## 
##         Number       1000                                        ## 
##         StringList   {&quot;String value 1&quot;,&quot;String Value 2&quot;}         ## 
##                                                                  ## 
######################################################################
##                                                                  ## 
## This sample response file causes the Oracle Net Configuration    ##
## Assistant (NetCA) to complete an Oracle Net configuration during ##
## a custom install of the Oracle12c server which is similar to     ##
## what would be created by the NetCA during typical Oracle12c      ##
## install. It also documents all of the NetCA response file        ##
## variables so you can create your own response file to configure  ##
## Oracle Net during an install the way you wish.                   ##
##                                                                  ## 
###################################################################### 

[GENERAL]
RESPONSEFILE_VERSION=&quot;19.0&quot;
CREATE_TYPE=&quot;CUSTOM&quot;

#-------------------------------------------------------------------------------
# Name       : SHOW_GUI
# Datatype   : Boolean
# Description: This variable controls appearance/suppression of the NetCA GUI,
# Pre-req    : N/A
# Default    : TRUE
# Note:
# This must be set to false in order to run NetCA in silent mode. 
# This is a substitute of &quot;/silent&quot; flag in the NetCA command line.
# The command line flag has precedence over the one in this response file.
# This feature is present since 10.1.0.3.
#-------------------------------------------------------------------------------
#SHOW_GUI=false

#-------------------------------------------------------------------------------
# Name       : LOG_FILE
# Datatype   : String
# Description: If present, NetCA will log output to this file in addition to the
#        standard out.
# Pre-req    : N/A
# Default    : NONE
# Note:
#  This is a substitute of &quot;/log&quot; in the NetCA command line.
# The command line argument has precedence over the one in this response file.
# This feature is present since 10.1.0.3.
#-------------------------------------------------------------------------------
#LOG_FILE=&quot;&quot;$ORACLE_BASE/cfgtoollogs/netca/netca.log&quot;&quot;

[oracle.net.ca]
#INSTALLED_COMPONENTS;StringList;list of installed components
# The possible values for installed components are:
# &quot;net8&quot;,&quot;server&quot;,&quot;client&quot;,&quot;aso&quot;, &quot;cman&quot;, &quot;javavm&quot; 
INSTALLED_COMPONENTS={&quot;server&quot;,&quot;net8&quot;,&quot;javavm&quot;}

#INSTALL_TYPE;String;type of install
# The possible values for install type are:
# &quot;typical&quot;,&quot;minimal&quot; or &quot;custom&quot;
INSTALL_TYPE=&quot;&quot;typical&quot;&quot;

#LISTENER_NUMBER;Number;Number of Listeners
# A typical install sets one listener 
LISTENER_NUMBER=1

#LISTENER_NAMES;StringList;list of listener names
# The values for listener are:
# &quot;LISTENER&quot;,&quot;LISTENER1&quot;,&quot;LISTENER2&quot;,&quot;LISTENER3&quot;, ...
# A typical install sets only &quot;LISTENER&quot; 
LISTENER_NAMES={&quot;{{ listener_name }}&quot;}

#LISTENER_PROTOCOLS;StringList;list of listener addresses (protocols and parameters separated by semicolons)
# The possible values for listener protocols are:
# &quot;TCP;1521&quot;,&quot;TCPS;2484&quot;,&quot;NMP;ORAPIPE&quot;,&quot;IPC;IPCKEY&quot;,&quot;VI;1521&quot; 
# For multiple listeners, separate them with commas ex &quot;TCP;1521&quot;,&quot;TCPS;2484&quot;
# For multiple protocols in single listener, separate them with &quot;&amp;&quot; ex  &quot;TCP;1521&amp;TCPS;2484&quot;
# A typical install sets only &quot;TCP;1521&quot; 
LISTENER_PROTOCOLS={&quot;{{ listener_protocol }}&quot;}

#LISTENER_START;String;name of the listener to start, in double quotes
LISTENER_START=&quot;&quot;{{ listener_name }}&quot;&quot;

#NAMING_METHODS;StringList;list of naming methods
# The possible values for naming methods are: 
# LDAP, TNSNAMES, ONAMES, HOSTNAME, NOVELL, NIS, DCE
# A typical install sets only: &quot;TNSNAMES&quot;,&quot;ONAMES&quot;,&quot;HOSTNAMES&quot; 
# or &quot;LDAP&quot;,&quot;TNSNAMES&quot;,&quot;ONAMES&quot;,&quot;HOSTNAMES&quot; for LDAP
NAMING_METHODS={&quot;TNSNAMES&quot;,&quot;ONAMES&quot;,&quot;HOSTNAME&quot;}
...
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/running-net-configuration-assistant-using-response-files.html#GUID-86CD4F09-BEF8-40B4-905C-5DA86582CD74&quot; target=&quot;_blank&quot;&gt;netca&lt;/a&gt;&lt;/code&gt; (more information on the net configuration assistent can be found &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/netag/managing-oracle-net-services.html#GUID-D8861C13-A525-4A57-97FD-BD8F2DC5A662&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;).&lt;/li&gt;
    &lt;li&gt;Create a response file for the database configuration assistent, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##############################################################################
##                                                                          ##
##                            DBCA response file                            ##
##                            ------------------                            ##
## Copyright(c) Oracle Corporation 1998,2019. All rights reserved.          ##
##                                                                          ##
## Specify values for the variables listed below to customize               ##
## your installation.                                                       ##
##                                                                          ##
## Each variable is associated with a comment. The comment                  ##
## can help to populate the variables with the appropriate                  ##
## values.                                                                  ##
##                                                                          ##
## IMPORTANT NOTE: This file contains plain text passwords and              ##
## should be secured to have read permission only by oracle user            ##
## or db administrator who owns this installation.                          ##
##############################################################################
#-------------------------------------------------------------------------------
# Do not change the following system generated value. 
#-------------------------------------------------------------------------------
responseFileVersion=/oracle/assistants/rspfmt_dbca_response_schema_v19.0.0

#-----------------------------------------------------------------------------
# Name          : gdbName
# Datatype      : String
# Description   : Global database name of the database
# Valid values  : &amp;lt;db_name&amp;gt;.&amp;lt;db_domain&amp;gt; - when database domain isn&#39;t NULL
#                 &amp;lt;db_name&amp;gt;             - when database domain is NULL
# Default value : None
# Mandatory     : Yes
#-----------------------------------------------------------------------------
gdbName={{ global_database_name }}

#-----------------------------------------------------------------------------
# Name          : sid
# Datatype      : String
# Description   : System identifier (SID) of the database
# Valid values  : Check Oracle19c Administrator&#39;s Guide
# Default value : &amp;lt;db_name&amp;gt; specified in GDBNAME
# Mandatory     : No
#-----------------------------------------------------------------------------
sid={{ database_sid }}

#-----------------------------------------------------------------------------
# Name          : databaseConfigType
# Datatype      : String
# Description   : database conf type as Single Instance, Real Application Cluster or Real Application Cluster One Nodes database
# Valid values  : SI\RAC\RACONENODE
# Default value : SI
# Mandatory     : No
#-----------------------------------------------------------------------------
databaseConfigType={{ database_config_type }}
...
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Run &lt;code&gt;&lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/running-database-configuration-assistant-using-response-files.html#GUID-E84CE996-B30C-4DCA-AE4C-1E90201317C2&quot; target=&quot;_blank&quot;&gt;dbca&lt;/a&gt;&lt;/code&gt; (more information on the database configuration assistent can be found &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/admin/creating-and-configuring-an-oracle-database.html#GUID-5DD346B8-4404-48F1-A811-F39D739E4D05&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following 

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/ansible/roles/oracle_database_config/tasks/main.yml

- name: create silent listener config file
  template:
    src: roles/oracle_database_config/templates/netca_rsp_{{ software_version }}.j2 
    dest: &quot;{{ silent_listener_config_file }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0644
  register: silent_listener_config_file_created

- name: configure listener
  become: true
  become_user: &quot;{{ oracle_install_user }}&quot;
  become_method: sudo
  command: /bin/sh -c &quot;{{ net_creation_assistent }} -silent -responsefile {{ silent_listener_config_file }}&quot;
    creates=&quot;{{ listener_ora }}&quot;
  when: silent_listener_config_file_created is success
  register: listener_configured
  
- name: debug configure listener
  debug:
    var=listener_configured
  when: listener_configured is changed

- name: create silent database config file
  template:
    src: roles/oracle_database_config/templates/dbca_rsp_{{ software_version }}.j2 
    dest: &quot;{{ silent_database_config_file }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0644
  register: silent_database_config_file_created

- name: configure oracle database
  become: true
  become_user: &quot;{{ oracle_install_user }}&quot;
  become_method: sudo
  command: /bin/sh -c &quot;{{ database_creation_assistent }} -silent -createDatabase -responseFile {{ silent_database_config_file }}&quot;
    creates=&quot;{{ sp_file }}&quot;
  when: silent_database_config_file_created is success
  register: oracle_database_configured
  
- name: debug configure oracle database
  debug:
    var=oracle_database_configured
  when: oracle_database_configured is changed
    
- name: create has script
  template:
    src: roles/oracle_database_config/templates/has_service.j2
    dest: &quot;{{ has_service }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0744
    
- name: create database script
  template:
    src: roles/oracle_database_config/templates/database_service.j2 
    dest: &quot;{{ database_service }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0744
    
- name: copy scripts
  copy: 
    src: roles/oracle_database_config/files/oracle/lifecycle/
    dest: &quot;{{ scripts_directory }}&quot;
    owner: &quot;{{ oracle_install_user }}&quot;
    group: &quot;{{ oracle_install_group }}&quot;
    mode: 0744

- name: delete temporary setup files that contain usernames and passwords
  file:
    path: &quot;{{ item }}&quot;
    state: absent
  loop:
    - &quot;{{ silent_listener_config_file }}&quot;
    - &quot;{{ silent_database_config_file }}&quot;
&lt;/pre&gt;

&lt;h2&gt;Run Ansible&lt;/h2&gt;
A good way to keep production environments and other environments separate is to use separate inventory files. In our example the inventory file for test has the following contents

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/ansible/inventories/test/hosts.cfg

[database_host]
datanode

[database_hosts:children]
database_host

[all:children]
database_hosts
&lt;/pre&gt;

Note that we use &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#group-and-host-variables&quot; target=&quot;_blank&quot;&gt;group_vars&lt;/a&gt; to override variables, for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##### User and group settings #####
oracle_install_password:         &quot;...&quot;

##### install options #####
oracle_hostname:                 &quot;{{ inventory_hostname  }}&quot;

##### configuration options #####
sys_password:                    &quot;...&quot;
system_password:                 &quot;...&quot;
&lt;/pre&gt;

Note that this file controls the differences between environments, such as address settings. This file also contains sensitive data, such as passwords, and as a result this file should be encypted. We can encrypt files by using &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/playbooks_vault.html&quot; target=&quot;_blank&quot;&gt;Vault&lt;/a&gt;. Vault is a feature of Ansible that allows keeping sensitive data such as passwords or keys in encrypted files, rather than as plaintext in roles. These vault files can then be distributed or placed in source control. To enable this feature, a command line tool, &lt;code&gt;ansible-vault&lt;/code&gt; is used to edit files, and a command line flag &lt;code&gt;--ask-vault-pass&lt;/code&gt; or &lt;code&gt;--vault-password-file&lt;/code&gt; is used. Alternately, we may specify the location of a password file or command Ansible to always prompt for the password in the &lt;code&gt;ansible.cfg&lt;/code&gt; file (&lt;code&gt;vault_password_file = /root/.rommel&lt;/code&gt;). This option requires no command line flag usage. To encrypt the file, we can use &lt;code&gt;ansible-vault encrypt filename.yml&lt;/code&gt;.
&lt;br/&gt;&lt;br/&gt;
The playbook that uses the roles defined above, has the following contents

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
##########
# database host configuration
# ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;host&quot;
#
# database installation
# ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;install&quot;
#
# database configuration
# ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;config&quot;

- hosts: database_hosts
  user: ansible
  become: true
  become_user: root
  become_method: sudo
  
  roles:
    - { role: oracle_database_vars, tags: [&#39;host&#39;,&#39;install&#39;,&#39;config&#39;] }
    - { role: oracle_database_host, tags: [&#39;host&#39;] }
    - { role: oracle_database_install, tags: [&#39;install&#39;] }
    - { role: oracle_database_config, tags: [&#39;config&#39;] }
&lt;/pre&gt;

in which, we have also defined &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html&quot; target=&quot;_blank&quot;&gt;tags&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
To configure the host, we use

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[ansible@datanode ansible]$ ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;host&quot;

PLAY [database_hosts] ******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_host : install necessary packages] *******************************************************************************************************************************************************
ok: [datanode] =&amp;gt; (item=binutils)
changed: [datanode] =&amp;gt; (item=compat-libcap1)
changed: [datanode] =&amp;gt; (item=compat-libstdc++-33)
ok: [datanode] =&amp;gt; (item=dos2unix)
ok: [datanode] =&amp;gt; (item=gcc)
changed: [datanode] =&amp;gt; (item=gcc-c++)
ok: [datanode] =&amp;gt; (item=glibc)
ok: [datanode] =&amp;gt; (item=glibc-devel)
ok: [datanode] =&amp;gt; (item=kmod)
changed: [datanode] =&amp;gt; (item=ksh)
ok: [datanode] =&amp;gt; (item=libaio)
changed: [datanode] =&amp;gt; (item=libaio-devel)
ok: [datanode] =&amp;gt; (item=libgcc)
ok: [datanode] =&amp;gt; (item=libstdc++)
ok: [datanode] =&amp;gt; (item=libstdc++-devel)
changed: [datanode] =&amp;gt; (item=libX11)
ok: [datanode] =&amp;gt; (item=libXau)
ok: [datanode] =&amp;gt; (item=libxcb)
changed: [datanode] =&amp;gt; (item=libXext)
changed: [datanode] =&amp;gt; (item=libXi)
changed: [datanode] =&amp;gt; (item=libXrender)
changed: [datanode] =&amp;gt; (item=libXtst)
changed: [datanode] =&amp;gt; (item=lsof)
ok: [datanode] =&amp;gt; (item=ksh)
ok: [datanode] =&amp;gt; (item=make)
ok: [datanode] =&amp;gt; (item=net-tools)
ok: [datanode] =&amp;gt; (item=perl)
changed: [datanode] =&amp;gt; (item=smartmontools)
changed: [datanode] =&amp;gt; (item=strace)
changed: [datanode] =&amp;gt; (item=sysstat)
changed: [datanode] =&amp;gt; (item=unzip)

TASK [oracle_database_host : create group] *********************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : create user] **********************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : set sysctl /etc/sysctl.d/10-default_sysctl.conf] **********************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : run sysctl -p] ********************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : set /etc/security/limits.conf] ****************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : check if iptables exists] *********************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_host : disable iptables] *****************************************************************************************************************************************************************
skipping: [datanode]

TASK [oracle_database_host : check if firewalld exists] ********************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_host : enable firewalld] *****************************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_host : activate public zone by adding interface to public zone] **************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : remove dhcpv6-client service from zone public] ************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : add database listen port to zone public] ******************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_host : enable selinux] *******************************************************************************************************************************************************************
changed: [datanode]

RUNNING HANDLER [oracle_database_host : restart server] ********************************************************************************************************************************************************
changed: [datanode]

RUNNING HANDLER [oracle_database_host : wait for server to restart] ********************************************************************************************************************************************
ok: [datanode -&amp;gt; localhost]

PLAY RECAP *****************************************************************************************************************************************************************************************************
datanode                       : ok=16   changed=11   unreachable=0    failed=0
&lt;/pre&gt;

To install the database software, we use

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[ansible@datanode ansible]$ ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;install&quot;

PLAY [database_hosts] ******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_install : create root directory] *********************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_install : create directories] ************************************************************************************************************************************************************
changed: [datanode] =&amp;gt; (item=/u01/repsonse_files_19.0.0)
changed: [datanode] =&amp;gt; (item=/u01/app/scripts)
changed: [datanode] =&amp;gt; (item=/u01/app/oracle/product/19.0.0/dbhome)

TASK [oracle_database_install : image based software installation] *********************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_install : rpm based software installation] ***********************************************************************************************************************************************
skipping: [datanode]

TASK [oracle_database_install : create silent install file] ****************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_install : 12c oracle database install] ***************************************************************************************************************************************************
skipping: [datanode]

TASK [oracle_database_install : debug 12c oracle database install] *********************************************************************************************************************************************
skipping: [datanode]

TASK [oracle_database_install : install oracle database] *******************************************************************************************************************************************************
[WARNING]: Module remote_tmp /home/oracle/.ansible/tmp did not exist and was created with a mode of 0700, this may cause issues when running as another user. To avoid this, create the remote_tmp dir with
the correct permissions manually
changed: [datanode]

TASK [oracle_database_install : debug install oracle database] *************************************************************************************************************************************************
ok: [datanode] =&amp;gt; {
    &quot;oracle_database_installed&quot;: {
        &quot;changed&quot;: true,
        &quot;cmd&quot;: [
            &quot;/bin/sh&quot;,
            &quot;-c&quot;,
            &quot;/u01/app/oracle/product/19.0.0/dbhome/runInstaller -silent -ignorePrereq -waitforcompletion -responseFile /u01/repsonse_files_19.0.0/silent-install.rsp&quot;
        ],
        &quot;delta&quot;: &quot;0:01:02.026905&quot;,
        &quot;end&quot;: &quot;2019-05-08 11:27:39.449180&quot;,
        &quot;failed&quot;: false,
        &quot;rc&quot;: 0,
        &quot;start&quot;: &quot;2019-05-08 11:26:37.422275&quot;,
        &quot;stderr&quot;: &quot;&quot;,
        &quot;stderr_lines&quot;: [],
        &quot;stdout&quot;: &quot;Launching Oracle Database Setup Wizard...\n\nThe response file for this session can be found at:\n /u01/app/oracle/product/19.0.0/dbhome/install/response/db_2019-05-08_11-26-37AM.rsp\n\nYou can find the log of this install session at:\n /tmp/InstallActions2019-05-08_11-26-37AM/installActions2019-05-08_11-26-37AM.log\n\nAs a root user, execute the following script(s):\n\t1. /u01/app/oraInventory/orainstRoot.sh\n\t2. /u01/app/oracle/product/19.0.0/dbhome/root.sh\n\nExecute /u01/app/oraInventory/orainstRoot.sh on the following nodes: \n[datanode]\nExecute /u01/app/oracle/product/19.0.0/dbhome/root.sh on the following nodes: \n[datanode]\n\n\nSuccessfully Setup Software.\nMoved the install session logs to:\n /u01/app/oraInventory/logs/InstallActions2019-05-08_11-26-37AM&quot;,
        &quot;stdout_lines&quot;: [
            &quot;Launching Oracle Database Setup Wizard...&quot;,
            &quot;&quot;,
            &quot;The response file for this session can be found at:&quot;,
            &quot; /u01/app/oracle/product/19.0.0/dbhome/install/response/db_2019-05-08_11-26-37AM.rsp&quot;,
            &quot;&quot;,
            &quot;You can find the log of this install session at:&quot;,
            &quot; /tmp/InstallActions2019-05-08_11-26-37AM/installActions2019-05-08_11-26-37AM.log&quot;,
            &quot;&quot;,
            &quot;As a root user, execute the following script(s):&quot;,
            &quot;\t1. /u01/app/oraInventory/orainstRoot.sh&quot;,
            &quot;\t2. /u01/app/oracle/product/19.0.0/dbhome/root.sh&quot;,
            &quot;&quot;,
            &quot;Execute /u01/app/oraInventory/orainstRoot.sh on the following nodes: &quot;,
            &quot;[datanode]&quot;,
            &quot;Execute /u01/app/oracle/product/19.0.0/dbhome/root.sh on the following nodes: &quot;,
            &quot;[datanode]&quot;,
            &quot;&quot;,
            &quot;&quot;,
            &quot;Successfully Setup Software.&quot;,
            &quot;Moved the install session logs to:&quot;,
            &quot; /u01/app/oraInventory/logs/InstallActions2019-05-08_11-26-37AM&quot;
        ]
    }
}

TASK [oracle_database_install : run orainstRoot.sh] ************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_install : run root.sh] *******************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_install : delete temporary setup files that contain usernames and passwords] *************************************************************************************************************
changed: [datanode] =&amp;gt; (item=/u01/repsonse_files_19.0.0/silent-install.rsp)

PLAY RECAP *****************************************************************************************************************************************************************************************************
datanode                       : ok=10    changed=10    unreachable=0    failed=0
&lt;/pre&gt;

To configure the database, we use

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[ansible@datanode ansible]$ ansible-playbook oracle_database.yml --inventory=inventories/test/hosts.cfg --tags &quot;config&quot;

PLAY [database_hosts] ******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************************************************
ok: [datanode]

TASK [oracle_database_config : create silent listener config file] *********************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : configure listener] *************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : debug configure listener] *******************************************************************************************************************************************************
ok: [datanode] =&amp;gt; {
    &quot;listener_configured&quot;: {
        &quot;changed&quot;: true,
        &quot;cmd&quot;: [
            &quot;/bin/sh&quot;,
            &quot;-c&quot;,
            &quot;/u01/app/oracle/product/19.0.0/dbhome/bin/netca -silent -responsefile /u01/repsonse_files_19.0.0/silent-listener-config.rsp&quot;
        ],
        &quot;delta&quot;: &quot;0:00:01.370024&quot;,
        &quot;end&quot;: &quot;2019-05-08 11:31:38.365648&quot;,
        &quot;failed&quot;: false,
        &quot;rc&quot;: 0,
        &quot;start&quot;: &quot;2019-05-08 11:31:36.995624&quot;,
        &quot;stderr&quot;: &quot;&quot;,
        &quot;stderr_lines&quot;: [],
        &quot;stdout&quot;: &quot;\nParsing command line arguments:\n    Parameter \&quot;silent\&quot; = true\n    Parameter \&quot;responsefile\&quot; = /u01/repsonse_files_19.0.0/silent-listener-config.rsp\nDone parsing command line arguments.\nOracle Net Services Configuration:\nProfile configuration complete.\nOracle Net Listener Startup:\n    Running Listener Control: \n      /u01/app/oracle/product/19.0.0/dbhome/bin/lsnrctl start LISTENER\n    Listener Control complete.\n    Listener started successfully.\nListener configuration complete.\nOracle Net Services configuration successful. The exit code is 0&quot;,
        &quot;stdout_lines&quot;: [
            &quot;&quot;,
            &quot;Parsing command line arguments:&quot;,
            &quot;    Parameter \&quot;silent\&quot; = true&quot;,
            &quot;    Parameter \&quot;responsefile\&quot; = /u01/repsonse_files_19.0.0/silent-listener-config.rsp&quot;,
            &quot;Done parsing command line arguments.&quot;,
            &quot;Oracle Net Services Configuration:&quot;,
            &quot;Profile configuration complete.&quot;,
            &quot;Oracle Net Listener Startup:&quot;,
            &quot;    Running Listener Control: &quot;,
            &quot;      /u01/app/oracle/product/19.0.0/dbhome/bin/lsnrctl start LISTENER&quot;,
            &quot;    Listener Control complete.&quot;,
            &quot;    Listener started successfully.&quot;,
            &quot;Listener configuration complete.&quot;,
            &quot;Oracle Net Services configuration successful. The exit code is 0&quot;
        ]
    }
}

TASK [oracle_database_config : create silent database config file] *********************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : configure oracle database] ******************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : debug configure oracle database] ************************************************************************************************************************************************
ok: [datanode] =&amp;gt; {
    &quot;oracle_database_configured&quot;: {
        &quot;changed&quot;: true,
        &quot;cmd&quot;: [
            &quot;/bin/sh&quot;,
            &quot;-c&quot;,
            &quot;/u01/app/oracle/product/19.0.0/dbhome/bin/dbca -silent -createDatabase -responseFile /u01/repsonse_files_19.0.0/silent-database-config.rsp&quot;
        ],
        &quot;delta&quot;: &quot;0:08:13.818718&quot;,
        &quot;end&quot;: &quot;2019-05-08 11:39:52.847777&quot;,
        &quot;failed&quot;: false,
        &quot;rc&quot;: 0,
        &quot;start&quot;: &quot;2019-05-08 11:31:39.029059&quot;,
        &quot;stderr&quot;: &quot;&quot;,
        &quot;stderr_lines&quot;: [],
        &quot;stdout&quot;: &quot;Prepare for db operation\n10% complete\nCopying database files\n40% complete\nCreating and starting Oracle instance\n42% complete\n46% complete\n50% complete\n54% complete\n60% complete\nCompleting Database Creation\n66% complete\n69% complete\n70% complete\nExecuting Post Configuration Actions\n100% complete\nDatabase creation complete. For details check the logfiles at:\n /u01/app/oracle/cfgtoollogs/dbca/ORCL.\nDatabase Information:\nGlobal Database Name:ORCL\nSystem Identifier(SID):ORCL\nLook at the log file \&quot;/u01/app/oracle/cfgtoollogs/dbca/ORCL/ORCL.log\&quot; for further details.&quot;,
        &quot;stdout_lines&quot;: [
            &quot;Prepare for db operation&quot;,
            &quot;10% complete&quot;,
            &quot;Copying database files&quot;,
            &quot;40% complete&quot;,
            &quot;Creating and starting Oracle instance&quot;,
            &quot;42% complete&quot;,
            &quot;46% complete&quot;,
            &quot;50% complete&quot;,
            &quot;54% complete&quot;,
            &quot;60% complete&quot;,
            &quot;Completing Database Creation&quot;,
            &quot;66% complete&quot;,
            &quot;69% complete&quot;,
            &quot;70% complete&quot;,
            &quot;Executing Post Configuration Actions&quot;,
            &quot;100% complete&quot;,
            &quot;Database creation complete. For details check the logfiles at:&quot;,
            &quot; /u01/app/oracle/cfgtoollogs/dbca/ORCL.&quot;,
            &quot;Database Information:&quot;,
            &quot;Global Database Name:ORCL&quot;,
            &quot;System Identifier(SID):ORCL&quot;,
            &quot;Look at the log file \&quot;/u01/app/oracle/cfgtoollogs/dbca/ORCL/ORCL.log\&quot; for further details.&quot;
        ]
    }
}

TASK [oracle_database_config : create has script] **************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : create database script] *********************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : copy scripts] *******************************************************************************************************************************************************************
changed: [datanode]

TASK [oracle_database_config : delete temporary setup files that contain usernames and passwords] **************************************************************************************************************
changed: [datanode] =&amp;gt; (item=/u01/repsonse_files_19.0.0/silent-listener-config.rsp)
changed: [datanode] =&amp;gt; (item=/u01/repsonse_files_19.0.0/silent-database-config.rsp)

PLAY RECAP *****************************************************************************************************************************************************************************************************
datanode                       : ok=11   changed=8    unreachable=0    failed=0
&lt;/pre&gt;

with the result (a running database)

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[oracle@datanode scripts]$ ./database_service status
The Oracle base has been set to /u01/app/oracle
Checking Database

LSNRCTL for Linux: Version 19.0.0.0.0 - Production on 08-MAY-2019 13:12:49

Copyright (c) 1991, 2019, Oracle.  All rights reserved.

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=datanode.ms.com)(PORT=1521)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 19.0.0.0.0 - Production
Start Date                08-MAY-2019 11:46:32
Uptime                    0 days 1 hr. 26 min. 16 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /u01/app/oracle/product/19.0.0/dbhome/network/admin/listener.ora
Listener Log File         /u01/app/oracle/diag/tnslsnr/datanode/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=datanode.ms.com)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
Services Summary...
Service &quot;ORCL&quot; has 1 instance(s).
  Instance &quot;ORCL&quot;, status READY, has 1 handler(s) for this service...
Service &quot;ORCLXDB&quot; has 1 instance(s).
  Instance &quot;ORCL&quot;, status READY, has 1 handler(s) for this service...
The command completed successfully
&lt;/pre&gt;

To test the set-up, we create a data source in WebLogic, i.e.,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[weblogic@javanode1 scripts]$ cat /u01/app/oracle/weblogic12.2.1/configuration/domains/tryout_domain/config/jdbc/data_source-jdbc.xml
&amp;lt;jdbc-data-source ...&amp;gt;
  &amp;lt;name&amp;gt;data_source&amp;lt;/name&amp;gt;
  &amp;lt;jdbc-driver-params&amp;gt;
    &amp;lt;url&amp;gt;jdbc:oracle:thin:@//datanode.ms.com:1521/orcl.ms.com&amp;lt;/url&amp;gt;
    &amp;lt;driver-name&amp;gt;oracle.jdbc.xa.client.OracleXADataSource&amp;lt;/driver-name&amp;gt;
    &amp;lt;properties&amp;gt;
      &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;user&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;example&amp;lt;/value&amp;gt;
      &amp;lt;/property&amp;gt;
    &amp;lt;/properties&amp;gt;
    &amp;lt;password-encrypted&amp;gt;{AES}iJxm6loPE9PHASnV4wMS4xkje9JxdNj/u1Vp9sW5lp0=&amp;lt;/password-encrypted&amp;gt;
  &amp;lt;/jdbc-driver-params&amp;gt;
  &amp;lt;jdbc-connection-pool-params&amp;gt;
    &amp;lt;initial-capacity&amp;gt;1&amp;lt;/initial-capacity&amp;gt;
    &amp;lt;max-capacity&amp;gt;15&amp;lt;/max-capacity&amp;gt;
    &amp;lt;min-capacity&amp;gt;1&amp;lt;/min-capacity&amp;gt;
    &amp;lt;connection-creation-retry-frequency-seconds&amp;gt;30&amp;lt;/connection-creation-retry-frequency-seconds&amp;gt;
    &amp;lt;test-table-name&amp;gt;SQL ISVALID&amp;lt;/test-table-name&amp;gt;
    &amp;lt;statement-cache-size&amp;gt;0&amp;lt;/statement-cache-size&amp;gt;
    &amp;lt;statement-cache-type&amp;gt;LRU&amp;lt;/statement-cache-type&amp;gt;
  &amp;lt;/jdbc-connection-pool-params&amp;gt;
  &amp;lt;jdbc-data-source-params&amp;gt;
    &amp;lt;jndi-name&amp;gt;jdbc/exampleDS&amp;lt;/jndi-name&amp;gt;
    &amp;lt;global-transactions-protocol&amp;gt;TwoPhaseCommit&amp;lt;/global-transactions-protocol&amp;gt;
  &amp;lt;/jdbc-data-source-params&amp;gt;
&amp;lt;/jdbc-data-source&amp;gt;
&lt;/pre&gt;

and use the application presented &lt;a href=&quot;http://middlewaresnippets.blogspot.com/2014/07/wildflying-java-ee-7.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; to issue some requests

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[weblogic@javanode1 scripts]$ cat /u01/app/oracle/weblogic12.2.1/configuration/domains/tryout_domain/servers/application_server_1/logs/application_server_1.out
...
RECEIVED OBJECT MESSAGE 145vbizoe7myg 5418
RECEIVED OBJECT MESSAGE fb6bol1924w4 708
RECEIVED OBJECT MESSAGE 1ch63dy8yqai9 2187
RECEIVED OBJECT MESSAGE 1g7gaw57fzotn 1331
RECEIVED OBJECT MESSAGE oguc0qagbp4u 9137
RECEIVED OBJECT MESSAGE bqvwlsb9zv89 7516
RECEIVED OBJECT MESSAGE dx4mjshkh1vk 7154
RECEIVED OBJECT MESSAGE 1oiykwu7nzfhk 7478
RECEIVED OBJECT MESSAGE 1wkdoab0estec 9915
RECEIVED OBJECT MESSAGE 19hi8q8q6x7yu 9398
RECEIVED OBJECT MESSAGE 1cbh6wevr0tpx 8587
RECEIVED OBJECT MESSAGE 1iw33pa5jzm1y 9239
RECEIVED OBJECT MESSAGE 1fgn7a86yvn3k 1431
RECEIVED OBJECT MESSAGE 1sxe62ll5vyz3 976
RECEIVED OBJECT MESSAGE uycy5zje69yt 5053
RECEIVED OBJECT MESSAGE 1vu33l45a4nv1 9714
RECEIVED OBJECT MESSAGE henvcnmrykhx 2779
RECEIVED OBJECT MESSAGE 1sysqs60tsjka 8564
RECEIVED OBJECT MESSAGE 162nwi88unxav 5078
RECEIVED OBJECT MESSAGE 1bev9nc1b2t6a 778

[oracle@datanode scripts]$ . /usr/local/bin/oraenv
ORACLE_SID = [oracle] ? orcl
The Oracle base has been set to /u01/app/oracle
[oracle@datanode scripts]$ sqlplus

SQL*Plus: Release 19.0.0.0.0 - Production on Mon May 20 13:27:52 2019
Version 19.3.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

Enter user-name: example
Enter password:
Last Successful login time: Mon May 20 2019 13:23:37 +02:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.3.0.0.0

SQL&amp;gt; describe persoon;
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 SOFINUMMER                                NOT NULL NUMBER(9)
 NAAM                                      NOT NULL VARCHAR2(50)
 FAMILYSOFINUMMER                                   NUMBER(9)

SQL&amp;gt; select sofinummer from persoon where naam like &#39;1bev9nc1b2t6a&#39;;

SOFINUMMER
----------
       778
&lt;/pre&gt;

Nah. No one will build a robot that does nothing but drink coffee - &lt;a href=&quot;https://dilbert.com/strip/2017-09-04&quot; target=&quot;_blank&quot;&gt;Wally&lt;/a&gt;.

&lt;h2&gt;References&lt;/h2&gt;
[1] &lt;a href=&quot;https://docs.ansible.com/&quot; target=&quot;_blank&quot;&gt;Ansible Documentation&lt;/a&gt;.&lt;/br&gt;
[2] &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/index.html&quot; target=&quot;_blank&quot;&gt;Oracle Database Documentation&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/1254510568171298233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/1254510568171298233'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/2019/05/ansibling-oracle-database.html' title='Ansibling the Oracle Database'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4896087204075281708.post-1160492498204264715</id><published>2019-04-07T14:44:00.001+02:00</published><updated>2019-05-01T11:33:13.301+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Apache HTTP Server"/><category scheme="http://www.blogger.com/atom/ns#" term="Automation"/><category scheme="http://www.blogger.com/atom/ns#" term="Database"/><category scheme="http://www.blogger.com/atom/ns#" term="Operating System"/><category scheme="http://www.blogger.com/atom/ns#" term="WebLogic"/><title type='text'>A Little Bit of Everything</title><content type='html'>In the following we give an example of how to set-up various platform components, such as,

&lt;ul&gt;
    &lt;li&gt;A &lt;a href=&quot;https://firewalld.org/&quot; target=&quot;_blank&quot;&gt;Firewall&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;An &lt;a href=&quot;https://www.nginx.com/&quot; target=&quot;_blank&quot;&gt;SSL Offloader and Proxy&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;A &lt;a href=&quot;http://httpd.apache.org/&quot; target=&quot;_blank&quot;&gt;Load Balancer&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;A &lt;a href=&quot;https://www.oracle.com/middleware/technologies/weblogic.html&quot; target=&quot;_blank&quot;&gt;JavaEE Application Server&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;A &lt;a href=&quot;https://www.mysql.com/&quot; target=&quot;_blank&quot;&gt;Database&lt;/a&gt; or another &lt;a href=&quot;https://www.postgresql.org/&quot; target=&quot;_blank&quot;&gt;Database&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;A &lt;a href=&quot;https://directory.fedoraproject.org/&quot; target=&quot;_blank&quot;&gt;Directory Server&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;An &lt;a href=&quot;https://mathias-kettner.com/&quot; target=&quot;_blank&quot;&gt;Application &amp; Infrastructure Monitoring&lt;/a&gt; tool.&lt;/li&gt;
&lt;/ul&gt;

In the example set-up, we use the following hosts

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[testuser@db1 ~]$ cat /etc/hosts
127.0.0.1         localhost
172.20.40.141     ansible.ms.com     ansible
172.20.40.142     dmz.ms.com         dmz
172.20.40.143     proxy.ms.com       proxy
172.20.40.144     java1.ms.com       java1
172.20.40.145     db1.ms.com         db1
172.20.40.146     java2.ms.com       java2
172.20.40.151     test151.ms.com     test151
&lt;/pre&gt;

An example architecture could be as follows

&lt;ul&gt;
    &lt;li&gt;The Ansible node runs as the name suggests our automation tool, in this case &lt;a href=&quot;https://www.ansible.com/&quot; target=&quot;_blank&quot;&gt;Ansible&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;The DMZ node runs the (main) Firewall, the SSL Offloader, and the (main) Proxy.&lt;/li&gt;
    &lt;li&gt;The Proxy node runs the Load Balancer.&lt;/li&gt;
    &lt;li&gt;The Java nodes run a JavaEE Application Server cluster.&lt;/li&gt;
    &lt;li&gt;The DB node runs the Databases and the Directory Server.&lt;/li&gt;
    &lt;li&gt;The test node runs the Application &amp; Infrastructure Monitoring tool.&lt;/li&gt;
&lt;/ul&gt;

Before, we begin we define the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html&quot; target=&quot;_blank&quot;&gt;Ansible Inventory&lt;/a&gt;:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[dmz_host]
dmz

[proxy_host]
proxy

[java1_host]
java1

[java2_host]
java2

[db1_host]
db1

[test_host]
test151

[weblogic_hosts:children]
java1_host
java2_host

[database_hosts:children]
db1_host

[all:children]
dmz_host
proxy_host
weblogic_hosts
database_hosts
test_host
&lt;/pre&gt;

Next, we need to create an SSH user (this user will be used by Ansible to connect to remote hosts and perform defined tasks):

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# create a new user, for example a user named ansible that belongs to a group named admin
$ sudo su - root
$ adduser ansible
$ groupadd admin
$ usermod -a -G admin ansible

# edit /etc/sudoers and add the admin group
## Allow root to run any commands anywhere
root    ALL=(ALL)           ALL
%admin  ALL=(ALL) NOPASSWD: ALL
# when using pipelining in Ansible disable requiretty
##Defaults    requiretty

# set-up ssh trust between Ansible clients
[ansible@hostname ~]$ ssh-keygen -t rsa -P &#39;&#39; -f ~/.ssh/id_rsa
Generating public/private rsa key pair.
Your identification has been saved in /home/ansible/.ssh/id_rsa.
Your public key has been saved in /home/ansible/.ssh/id_rsa.pub.

[ansible@different_hostname ~]$ mkdir -m 0700 .ssh
[ansible@different_hostname ~]$ install -m 0600 /dev/null .ssh/authorized_keys
[ansible@different_hostname ~]$ cat &amp;gt;&amp;gt; .ssh/authorized_keys
ssh-rsa ...
^C

# test ssh between Ansible clients
[ansible@hostname ~]$ ssh ansible@different_hostname hostname

# test Ansible
[ansible@hostname ansible]$ ansible all -m ping --inventory=inventories/development/demo_hosts.cfg
[ansible@hostname ansible]$ ansible all -a &quot;/bin/echo hello&quot; --inventory=inventories/development/demo_hosts.cfg
[ansible@hostname ansible]$ ansible all -m setup -a &#39;filter=*fqdn*&#39; --inventory=inventories/development/demo_hosts.cfg
&lt;/pre&gt;

&lt;h2&gt;Linux Tools&lt;/h2&gt;
Install some handy Linux Tools (such as &lt;a href=&quot;https://chrony.tuxfamily.org/&quot; target=&quot;_blank&quot;&gt;Chrony&lt;/a&gt;, and &lt;a href=&quot;https://packaging.python.org/tutorials/installing-packages/&quot; target=&quot;_blank&quot;&gt;Python Package Installer&lt;/a&gt;)

&lt;ul&gt;
    &lt;li&gt;Used Variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
demo_domain_name: &quot;ms.com&quot;
demo_domain_ips: &quot;172.20.40.0/24&quot;

dmz_hostname: &quot;dmz&quot;
dmz_ip_addr: &quot;172.20.40.142&quot;
dmz_fqdn: &quot;{{ dmz_hostname }}.{{ demo_domain_name }}&quot;

proxy_hostname: &quot;proxy&quot;
proxy_ip_addr: &quot;172.20.40.143&quot;
proxy_fqdn: &quot;{{ proxy_hostname }}.{{ demo_domain_name }}&quot;

java1_hostname: &quot;java1&quot;
java1_ip_addr: &quot;172.20.40.144&quot;
java1_fqdn: &quot;{{ java1_hostname }}.{{ demo_domain_name }}&quot;

java2_hostname: &quot;java2&quot;
java2_ip_addr: &quot;172.20.40.146&quot;
java2_fqdn: &quot;{{ java2_hostname }}.{{ demo_domain_name }}&quot;

db1_hostname: &quot;db1&quot;
db1_ip_addr: &quot;172.20.40.145&quot;
db1_fqdn: &quot;{{ db1_hostname }}.{{ demo_domain_name }}&quot;

test_hostname: &quot;test151&quot;
test_ip_addr: &quot;172.20.40.151&quot;
test_fqdn: &quot;{{ test_hostname }}.{{ demo_domain_name }}&quot;

linux_tool_packages:
  - chrony
  - dos2unix
  - mlocate
  - openldap-clients
  - python-pip
  - python-wheel
  - acme-tiny
  - traceroute
  - yum-utils  

python_libraries:
  - pyopenssl
  - pymysql
  - psycopg2-binary
  - passlib

hardening_packages:
  - firewalld
  - policycoreutils
  - selinux-policy
  - selinux-policy-targeted
  - libselinux-utils
  - libselinux-python
  - setroubleshoot-server
  - setools-console
  - mcstrans
  - fail2ban-firewalld

# chrony server pool
chrony_server_0: &quot;server 0.nl.pool.ntp.org iburst&quot;
chrony_server_1: &quot;server 1.nl.pool.ntp.org iburst&quot;
chrony_server_2: &quot;server 2.nl.pool.ntp.org iburst&quot;
chrony_server_3: &quot;server 3.nl.pool.ntp.org iburst&quot;

# chrony server
chrony_server: &quot;server {{ dmz_fqdn }} iburst&quot;

# chrony, allow NTP client access from local network
chrony_allow: &quot;172.20/16&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure &lt;a href=&quot;http://www.ntp.org/&quot; target=&quot;_blank&quot;&gt;NTP&lt;/a&gt;
        &lt;ul&gt;
            &lt;li&gt;Install &lt;a href=&quot;http://chrony.tuxfamily.org/&quot; target=&quot;_blank&quot;&gt;Chrony&lt;/a&gt;&lt;/li&gt;
            &lt;li&gt;On the DMZ node, we configure chrony (&lt;code&gt;/etc/chrony.conf&lt;/code&gt;) by using the following template:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
{{ chrony_server_0 }}
{{ chrony_server_1 }}
{{ chrony_server_2 }}
{{ chrony_server_3 }}

# UDP port on which the server understands NTP requests
port 123

# Ignore stratum in source selection.
stratumweight 0

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Enable kernel RTC synchronization.
rtcsync

# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3

# Allow NTP client access from local network.
allow {{ chrony_allow }}

# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

# Serve time even if not synchronized to any NTP server.
#local stratum 10

keyfile /etc/chrony.keys

# Specify the key used as password for chronyc.
commandkey 1

# Generate command key if missing.
generatecommandkey

# Disable logging of client accesses.
noclientlog

# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5

logdir /var/log/chrony
#log measurements statistics tracking
&lt;/pre&gt;
            details about public NTP servers can be found &lt;a href=&quot;http://www.pool.ntp.org/en/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; (the &lt;code&gt;pool.ntp.org&lt;/code&gt; project is a big virtual cluster of timeservers providing a reliable NTP service).&lt;/li&gt;
            &lt;li&gt;On the other nodes we use the following template:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
{{ chrony_server }}

# UDP port on which the server understands NTP requests
port 123
 
# Ignore stratum in source selection.
stratumweight 0
 
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
 
# Enable kernel RTC synchronization.
rtcsync
 
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
 
# Allow NTP client access from local network.
#allow 192.168/16
 
# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
 
# Serve time even if not synchronized to any NTP server.
#local stratum 10
 
keyfile /etc/chrony.keys
 
# Specify the key used as password for chronyc.
commandkey 1
 
# Generate command key if missing.
generatecommandkey
 
# Disable logging of client accesses.
noclientlog
 
# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5
 
logdir /var/log/chrony
#log measurements statistics tracking
&lt;/pre&gt;
            &lt;/li&gt;
            &lt;li&gt;Start chrony.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install extra packages for enterprise linux repository configuration
  yum:
    name=https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    state=present

- name: install tools
  yum:
    name: &quot;{{ linux_tool_packages }}&quot;
    state: present
    
- name: upgrade pip
  pip:
    name: pip
    state: latest
  
- name: install python libraries
  pip:
    name: &quot;{{ python_libraries }}&quot;
    state: latest
  
- name: edit server pool /etc/chrony.conf
  template: 
    src: roles/demo_linux_tools/templates/chrony_server_pool_conf.j2
    dest: /etc/chrony.conf
    owner: root
    group: root
    mode: 0644
  register: chrony_conf_dmz_changed
  when: inventory_hostname == dmz_hostname
  
- name: edit server /etc/chrony.conf
  template: 
    src: roles/demo_linux_tools/templates/chrony_server_conf.j2
    dest: /etc/chrony.conf
    owner: root
    group: root
    mode: 0644
  register: chrony_conf_proxy_changed
  when: inventory_hostname != dmz_hostname

- name: enable and start chrony service
  systemd:
    name: chronyd
    state: started
    enabled: yes
  register: chrony_started
  
- name: restart chrony service
  systemd:
    name: chronyd
    state: restarted
  when: chrony_started is success and (chrony_conf_dmz_changed is changed or chrony_conf_proxy_changed is changed)
&lt;/pre&gt;

The &lt;code&gt;python_libraries&lt;/code&gt; variable contains a list of python packages that are needed by Ansible modules, such as &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/postgresql_db_module.html&quot; target=&quot;_blank&quot;&gt;postgresql_db&lt;/a&gt; (see the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/postgresql_db_module.html#requirements&quot; target=&quot;_blank&quot;&gt;requirements&lt;/a&gt; section of a particular module for more information).

&lt;h2&gt;DMZ&lt;/h2&gt;
To set-up the DMZ, we use the following:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# ssh
sshd_permit_root_login: &quot;yes&quot;
sshd_allow_groups: &quot;root admin {{ ds_operator_group }}&quot;
sshd_authorized_keys_command: &quot;/usr/local/bin/get-sshkey-from-ldap.sh&quot;

# selinux configuratie
selinux_state: &quot;permissive&quot;
#selinux_state: &quot;enforcing&quot;
selinux_policy: &quot;targeted&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure the &lt;a href=&quot;https://www.freebsd.org/cgi/man.cgi?sshd_config(5)&quot; target=&quot;_blank&quot;&gt;sshd&lt;/a&gt;, here we set variables such as &lt;code&gt;sshd_permit_root_login&lt;/code&gt;, &lt;code&gt;sshd_allow_groups&lt;/code&gt;, and &lt;code&gt;sshd_authorized_keys_command&lt;/code&gt;. To edit these parameters we create a template based on the existing &lt;code&gt;sshd_config&lt;/code&gt; file, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/local/bin:/usr/bin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

# If you want to change the port on a SELinux system, you have to tell
# SELinux about this change.
# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER
#
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
SyslogFacility AUTHPRIV
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#AllowGroups {{ sshd_allow_groups }}
PermitRootLogin {{ sshd_permit_root_login }}
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys

#AuthorizedPrincipalsFile none

AuthorizedKeysCommand {{ sshd_authorized_keys_command }}
AuthorizedKeysCommandUser {{ root_user }}
...
&lt;/pre&gt;
    The authorized keys command calls the following script
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#!/bin/sh

ldapsearch -h {{ db1_fqdn }} -p {{ ds_directory_server_network_port }}  -b &quot;{{ ds_suffix }}&quot; -x &quot;(&amp;(objectClass=ldapPublicKey)(uid=$1))&quot; &quot;sshPublicKey&quot; | sed -n &#39;/^ /{H;d};/sshPublicKey:/x;$g;s/\n *//g;s/sshPublicKey: //gp&#39;
&lt;/pre&gt;
    which obtains the ssh public key from the Directory Server for a given user.
    &lt;/li&gt;
    &lt;li&gt;Add an extra sudoers in the &lt;code&gt;/etc/sudoers.d&lt;/code&gt; directory
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
## Command Aliases
## These are groups of related commands

## Networking
Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool

## Installation and management of software
Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum

## Services
Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig, /usr/bin/systemctl start, /usr/bin/systemctl stop, /usr/bin/systemctl reload, /usr/bin/systemctl restart, /usr/bin/systemctl status, /usr/bin/systemctl enable, /usr/bin/systemctl disable

## Updating the locate database
Cmnd_Alias LOCATE = /usr/bin/updatedb

## Storage
Cmnd_Alias STORAGE = /sbin/fdisk, /sbin/sfdisk, /sbin/parted, /sbin/partprobe, /bin/mount, /bin/umount

## Delegating permissions
Cmnd_Alias DELEGATING = /usr/sbin/visudo, /bin/chown, /bin/chmod, /bin/chgrp

## Processes
Cmnd_Alias PROCESSES = /bin/nice, /bin/kill, /usr/bin/kill, /usr/bin/killall

## Drivers
Cmnd_Alias DRIVERS = /sbin/modprobe

## Allows members of the &#39;operators&#39; group to run networking, software, service management apps and more.
%operators ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Edit Kernel parameters, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled.  See sysctl(8) and
# sysctl.conf(5) for more details.

# Controls IP packet forwarding
# net.ipv4.ip_forward = 0

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename.
# Useful for debugging multi-threaded applications.
kernel.core_uses_pid = 1

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1

# Controls the default maxmimum size of a mesage queue
kernel.msgmnb = 65536

# Controls the maximum size of a message, in bytes
kernel.msgmax = 65536

# Controls the maximum shared segment size, in bytes
kernel.shmmax = 68719476736

# Controls the maximum number of shared memory segments, in pages
kernel.shmall = 4294967296

# semaphores
kernel.sem = 250 32000 100 128
# shared memory
kernel.shmmni = 4096

# file handles
fs.file-max = 6815744

# asynchronous i/o
fs.aio-max-nr = 1048576

# disable ipv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

# default range of IP port numbers that are allowed for TCP and UDP traffic
net.ipv4.ip_local_port_range = 9000 65535

# increase TCP max buffer size (depending on the type of NIC and the round-trip time these values can be changed)
# Maximum TCP Receive Window
net.core.rmem_max = 8388608
net.core.rmem_default = 8388608
# Maximum TCP Send Window
net.core.wmem_max = 8388608
net.core.wmem_default = 8388608
#  memory reserved for TCP receive buffers (vector of 3 integers: [min, default, max])
net.ipv4.tcp_rmem = 4096 87380 8388608
# memory reserved for TCP send buffers (vector of 3 integers: [min, default, max])
net.ipv4.tcp_wmem = 4096 87380 8388608

# increase the length of the processor input queue
net.core.netdev_max_backlog = 30000
# maximum amount of memory buffers (could be set equal to net.core.rmem_max and net.core.wmem_max)
net.core.optmem_max = 20480
# socket of the listen backlog
net.core.somaxconn = 1024

# tcp selective acknowledgements (disable them on high-speed networks)
net.ipv4.tcp_sack = 1
net.ipv4.tcp_dsack = 1
# Timestamps add 12 bytes to the TCP header
net.ipv4.tcp_timestamps = 1
# Support for large TCP Windows - Needs to be set to 1 if the Max TCP Window is over 65535
net.ipv4.tcp_window_scaling = 1
# The interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe
net.ipv4.tcp_keepalive_time = 300
# The interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime
net.ipv4.tcp_keepalive_intvl = 30
# The number of unacknowledged probes to send before considering the connection dead and notifying the application layer
net.ipv4.tcp_keepalive_probes = 5
# The time that must elapse before TCP/IP can release a closed connection and reuse its resources.
net.ipv4.tcp_fin_timeout = 30
# Size of the backlog connections queue.
net.ipv4.tcp_max_syn_backlog = 4096
# The tcp_tw_reuse setting is particularly useful in environments where numerous short connections are open and left in TIME_WAIT state, such as web servers.
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1

# The percentage of how aggressively memory pages are swapped to disk
vm.swappiness = 0
# The percentage of main memory the pdflush daemon should write data out to the disk.
vm.dirty_background_ratio = 25
# The percentage of main memory the actual disk writes will take place.
vm.dirty_ratio = 20
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Edit Linux resources, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# /etc/security/limits.conf
#
#This file sets the resource limits for the users logged in via PAM.
#It does not affect resource limits of the system services.
#
#Also note that configuration files in /etc/security/limits.d directory,
#which are read in alphabetical order, override the settings in this
#file in case the domain is the same or more specific.
#That means for example that setting a limit for wildcard domain here
#can be overriden with a wildcard setting in a config file in the
#subdirectory, but a user specific setting here can be overriden only
#with a user specific setting in the subdirectory.
#
#Each line describes a limit for a user in the form:
#
#&amp;lt;domain&amp;gt;        &amp;lt;type&amp;gt;  &amp;lt;item&amp;gt;  &amp;lt;value&amp;gt;
#
#Where:
#&amp;lt;domain&amp;gt; can be:
#        - a user name
#        - a group name, with @group syntax
#        - the wildcard *, for default entry
#        - the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#
#&amp;lt;type&amp;gt; can have the two values:
#        - &quot;soft&quot; for enforcing the soft limits
#        - &quot;hard&quot; for enforcing hard limits
#
#&amp;lt;item&amp;gt; can be one of the following:
#        - core - limits the core file size (KB)
#        - data - max data size (KB)
#        - fsize - maximum filesize (KB)
#        - memlock - max locked-in-memory address space (KB)
#        - nofile - max number of open file descriptors
#        - rss - max resident set size (KB)
#        - stack - max stack size (KB)
#        - cpu - max CPU time (MIN)
#        - nproc - max number of processes
#        - as - address space limit (KB)
#        - maxlogins - max number of logins for this user
#        - maxsyslogins - max number of logins on the system
#        - priority - the priority to run user process with
#        - locks - max number of file locks the user can hold
#        - sigpending - max number of pending signals
#        - msgqueue - max memory used by POSIX message queues (bytes)
#        - nice - max nice priority allowed to raise to values: [-20, 19]
#        - rtprio - max realtime priority
#
#&amp;lt;domain&amp;gt;      &amp;lt;type&amp;gt;  &amp;lt;item&amp;gt;         &amp;lt;value&amp;gt;
#

#*               soft    core            0
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#@student        -       maxlogins       4

*               soft     nofile          8192
*               hard     nofile          8192

# End of file
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure the Firewall. To this end, we use the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/firewalld_module.html&quot; target=&quot;_blank&quot;&gt;firewalld&lt;/a&gt; module, and add services such as https, ntp, ldap.&lt;/li&gt;
    &lt;li&gt;Configure SELinux and set the &lt;code&gt;httpd_can_network_connect&lt;/code&gt; and &lt;code&gt;httpd_can_network_relay&lt;/code&gt; flags. To this end, we use the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/selinux_module.html&quot; target=&quot;_blank&quot;&gt;selinux&lt;/a&gt; and the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/seboolean_module.html&quot; target=&quot;_blank&quot;&gt;seboolean&lt;/a&gt; modules.&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following 
 
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install hardening packages
  yum:
    name: &quot;{{ hardening_packages }}&quot;
    state: present

# secure ssh
- name: edit /etc/ssh/sshd_config
  template: 
    src: roles/demo_linux_hardening/templates/sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: 0600
  register: sshd_edited

- name: add /usr/local/bin/get-sshkey-from-ldap.sh
  template:
    src: roles/demo_linux_hardening/templates/get_sshkey_from_ldap_sh.j2
    dest: &quot;{{ sshd_authorized_keys_command }}&quot;
    owner: root
    group: root
    mode: 0700

- name: restart sshd service
  systemd:
    name: sshd
    state: restarted
  when: sshd_edited is changed

# sudoers
- name: add /etc/sudoers.d/demo_sudoers
  template:
    src: roles/demo_linux_hardening/templates/demo_sudoers.j2
    dest: /etc/sudoers.d/demo_sudoers
    owner: root
    group: root
    mode: 0440

# kernel parameters
- name: set sysctl /etc/sysctl.d/10-default_sysctl.conf
  template: 
    src: roles/demo_linux_hardening/templates/default_sysctl_conf.j2
    dest: /etc/sysctl.d/10-default_sysctl.conf
    owner: root
    group: root
    mode: 0644

- name: enable ip forwarding
  sysctl:
    name: net.ipv4.ip_forward
    value: 1
    sysctl_file: /etc/sysctl.d/11-dmz_proxy_sysctl.conf
    sysctl_set: yes
    state: present
    reload: yes
  when: inventory_hostname == dmz_hostname or inventory_hostname == proxy_hostname

# linux resource limits
- name: set /etc/security/limits.conf
  template:
    src: roles/demo_linux_hardening/templates/limits_conf.j2
    dest: /etc/security/limits.conf
    owner: root
    group: root
    mode: 0644

# configure firewall
- name: check if iptables exists
  stat: path=/usr/lib/systemd/system/iptables.service
  register: iptables_status
    
- name: disable iptables
  service:
    name=iptables
    state=stopped
    enabled=no
  when: iptables_status.stat.exists
  
- name: check if firewalld exists
  stat: path=/usr/lib/systemd/system/firewalld.service
  register: firewalld_status

- name: enable firewalld
  systemd:
    name: firewalld
    state: started
    enabled: yes
  when: firewalld_status.stat.exists

- name: activate public zone by adding interface to public zone
  firewalld:
    zone: public
    interface: &quot;{{ ansible_default_ipv4.interface }}&quot;
    state: enabled
    permanent: yes
    immediate: yes
    
- name: remove dhcpv6-client service from zone public
  firewalld:
    zone: public
    service: dhcpv6-client
    state: disabled
    permanent: yes
    immediate: yes
    
- name: add http service to zone public
  firewalld:
    zone: public
    service: http
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == dmz_hostname

- name: add https service to zone public
  firewalld:
    zone: public
    service: https
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == dmz_hostname

- name: add ntp service to zone public
  firewalld:
    zone: public
    service: ntp
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == dmz_hostname
  
- name: add rich rule to forward all ipv4 traffic on port 80 to port 8080
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 forward-port port=80 protocol=tcp to-port={{ nginx_http_listen_port }}&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == dmz_hostname
  
- name: add rich rule to forward all ipv4 traffic on port 443 to port 8443
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 forward-port port=443 protocol=tcp to-port={{ nginx_listen_port }}&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == dmz_hostname

- name: add rich rule to accept request from certain host on port 8080
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port={{ apache_listen_port }} protocol=tcp source address={{ dmz_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == proxy_hostname
  
- name: add rich rule to accept request from java1 host on port 5556
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=5556 protocol=tcp source address={{ java1_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from java2 host on port 5556
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=5556 protocol=tcp source address={{ java2_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from java1 host on port 7001
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=7001 protocol=tcp source address={{ java1_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from java2 host on port 7001
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=7001 protocol=tcp source address={{ java2_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from proxy host on port 7001
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=7001 protocol=tcp source address={{ proxy_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from java1 host on port 9002-9003
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=9002-9003 protocol=tcp source address={{ java1_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from java2 host on port 9002-9003
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=9002-9003 protocol=tcp source address={{ java2_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname
  
- name: add rich rule to accept request from proxy host on port 9002-9003
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port=9002-9003 protocol=tcp source address={{ proxy_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == java1_hostname or inventory_hostname == java2_hostname

- name: add ldap service to zone public
  firewalld:
    zone: public
    service: ldap
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == db1_hostname
  
- name: add mysql port to zone public
  firewalld:
    zone: public
    port: 3306/tcp
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == db1_hostname
  
- name: add postgresql port to zone public
  firewalld:
    zone: public
    port: 5432/tcp
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == db1_hostname

- name: add rich rule to accept request from certain host on port 80
  firewalld:
    zone: public
    rich_rule: &#39;rule family=ipv4 port port={{ cmk_listen_port }} protocol=tcp source address={{ proxy_ip_addr }} accept&#39;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == test_hostname
  
- name: add ldap and ldaps to zone public
  firewalld:
    zone: public
    service: &quot;{{ item }}&quot;
    state: enabled
    permanent: yes
    immediate: yes
  when: inventory_hostname == test_hostname
  loop:
    - ldap
    - ldaps
    
# configure selinux
- name: enable selinux
  selinux:
    policy: &quot;{{ selinux_policy }}&quot;
    state: &quot;{{ selinux_state }}&quot;
  notify:
    - restart server
    - wait for server to restart

- name: set httpd_can_network_connect flag on and keep it persistent across reboots
  seboolean:
    name: httpd_can_network_connect
    state: yes
    persistent: yes
    
- name: set httpd_can_network_relay flag on and keep it persistent across reboots
  seboolean:
    name: httpd_can_network_relay
    state: yes
    persistent: yes


# handler that is called when seliux settings are changed
- name: restart server
  shell: sleep 2 &amp;&amp; shutdown -r now &quot;Ansible updates triggered&quot;
  async: 1
  poll: 0
  ignore_errors: True
  
- name: wait for server to restart
  local_action: wait_for
    host={{ inventory_hostname }}
    port=22
    state=started
    delay=10
    timeout=300
  become: false
&lt;/pre&gt;
 
To check the firewall configuration we can use &lt;code&gt;firewall-cmd&lt;/code&gt; (see &lt;a href=&quot;https://firewalld.org/documentation/man-pages/firewall-cmd.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information):

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@dmz ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: eth0
  sources:
  services: ssh https ntp http
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:
        rule family=&quot;ipv4&quot; forward-port port=&quot;443&quot; protocol=&quot;tcp&quot; to-port=&quot;8443&quot;
        rule family=&quot;ipv4&quot; forward-port port=&quot;80&quot; protocol=&quot;tcp&quot; to-port=&quot;8080&quot;
&lt;/pre&gt;

More information on using firewalls and getting started with firewalld can be found &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/security_guide/sec-using_firewalls#sec-Introduction_to_firewalld&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt; 
When configuring SELinux it is wise to first run SELinux in &lt;code&gt;Permissive&lt;/code&gt; mode to obtain audit logging before setting it to &lt;code&gt;Enforcing&lt;/code&gt;, i.e., access a certain host and check what is happening in the audit logging (&lt;code&gt;/var/log/audit/audit.log&lt;/code&gt;), for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
type=AVC msg=audit(1552124629.566:127): avc:  denied  { name_connect } for  pid=6566 comm=&quot;nginx&quot; dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket permissive=1
type=SYSCALL msg=audit(1552124629.566:127): arch=c000003e syscall=42 success=no exit=-115 a0=b a1=7f7c5ed40080 a2=10 a3=7fffb8e8f210 items=0 ppid=6565 pid=6566 auid=4294967295 uid=996 gid=993 euid=996 suid=996 fsuid=996 egid=993 sgid=993 fsgid=993 tty=(none) ses=4294967295 comm=&quot;nginx&quot; exe=&quot;/usr/sbin/nginx&quot; subj=system_u:system_r:httpd_t:s0 key=(null)
type=PROCTITLE msg=audit(1552124629.566:127): proctitle=6E67696E783A20776F726B65722070726F63657373
&lt;/pre&gt;

Using: &lt;code&gt;grep 1552124629.566:127 /var/log/audit/audit.log | audit2why&lt;/code&gt; gives:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
type=AVC msg=audit(1552124629.566:127): avc:  denied  { name_connect } for  pid=6566 comm=&quot;nginx&quot; dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket permissive=1

Was caused by:
One of the following booleans was set incorrectly.
Description:
Allow httpd to can network connect

Allow access by executing:
# setsebool -P httpd_can_network_connect 1
Description:
Allow httpd to can network relay

Allow access by executing:
# setsebool -P httpd_can_network_relay 1
&lt;/pre&gt;
    
The output from &lt;code&gt;audit2why&lt;/code&gt; indicates that we can allow Nginx to make proxy connections by enabling one or both of the &lt;code&gt;httpd_can_network_relay&lt;/code&gt; and &lt;code&gt;httpd_can_network_connect&lt;/code&gt; Boolean options. We can enable them either temporarily or permanently, the latter by adding the -P flag as shown in the output. The &lt;code&gt;sesearch&lt;/code&gt; command provides more information about the Boolean options:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@dmz ~]# sesearch -A -s httpd_t -b httpd_can_network_connect
Found 1 semantic av rules:
   allow httpd_t port_type : tcp_socket name_connect ;
&lt;/pre&gt;

This output indicates that &lt;code&gt;httpd_can_network_connect&lt;/code&gt; allows processes labeled with the &lt;code&gt;httpd_t&lt;/code&gt; context to connect to all TCP socket types that have the &lt;code&gt;port_type&lt;/code&gt; attribute.

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@dmz ~]# seinfo -aport_type -x
   port_type
      afs3_callback_port_t
      afs_bos_port_t
      afs_fs_port_t
      afs_ka_port_t
      afs_pt_port_t
      afs_vl_port_t
      agentx_port_t
      amanda_port_t
      ...

[root@dmz ~]# sesearch -A -s httpd_t -b httpd_can_network_relay
Found 17 semantic av rules:
   allow httpd_t http_port_t : tcp_socket name_connect ;
   allow httpd_t squid_port_t : tcp_socket name_connect ;
   allow httpd_t ephemeral_port_type : tcp_socket name_connect ;
   allow httpd_t ftp_port_t : tcp_socket name_connect ;
   allow httpd_t gopher_client_packet_t : packet recv ;
   allow httpd_t gopher_client_packet_t : packet send ;
   allow httpd_t http_cache_port_t : tcp_socket name_connect ;
   allow httpd_t squid_client_packet_t : packet recv ;
   allow httpd_t squid_client_packet_t : packet send ;
   allow httpd_t http_cache_client_packet_t : packet recv ;
   allow httpd_t http_cache_client_packet_t : packet send ;
   allow httpd_t gopher_port_t : tcp_socket name_connect ;
   allow httpd_t http_client_packet_t : packet recv ;
   allow httpd_t http_client_packet_t : packet send ;
   allow httpd_t memcache_port_t : tcp_socket name_connect ;
   allow httpd_t ftp_client_packet_t : packet recv ;
   allow httpd_t ftp_client_packet_t : packet send ;
&lt;/pre&gt;

This output indicates that &lt;code&gt;httpd_can_network_relay&lt;/code&gt; allows processes labeled with the &lt;code&gt;httpd_t&lt;/code&gt; context to connect to ports of various types, including type &lt;code&gt;http_port_t&lt;/code&gt;.
&lt;br/&gt;&lt;br/&gt;
By default, the SELinux configuration does not allow to listen to TCP or UDP ports other than the default ones that are whitelisted in the &lt;code&gt;http_port_t&lt;/code&gt; type:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@dmz ~]# semanage port -l | grep http_port_t
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t            tcp      5988

[root@dmz ~]# semanage port -a -t http_port_t -p tcp 8080
ValueError: Port tcp/8080 already defined

[root@dmz ~]# semanage port -l | grep 8080
http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010

# semanage port -a -t http_port_t -p tcp 8001
# https://docs.ansible.com/ansible/latest/modules/seport_module.html
- name: allow a process to listen on tcp port 8001
  seport:
    ports: 8001
    proto: tcp
    setype: http_port_t
    state: present
    reload: yes
&lt;/pre&gt;

SELinux troubleshooting is further explained in the article: &lt;a href=&quot;https://access.redhat.com/articles/2191331&quot; target=&quot;_blank&quot;&gt;Basic SELinux Troubleshooting in CLI&lt;/a&gt; (using the commands &lt;code&gt;ausearch -m AVC,USER_AVC&lt;/code&gt;, &lt;code&gt;ausearch -m AVC,USER_AVC -ts recent&lt;/code&gt;, and &lt;code&gt;journalctl -t setroubleshoot&lt;/code&gt;).
 &lt;br/&gt;&lt;br/&gt;
To configure Nginx we can use the following:
 
&lt;ul&gt;
    &lt;li&gt;Used Variables
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
nginx_repo_name: &quot;nginx-stable&quot;
nginx_repo_baseurl: &quot;http://nginx.org/packages/rhel/7/$basearch/&quot;
nginx_repo_gpgkey: &quot;&quot;

nginx_http_listen_port: &quot;8080&quot;
nginx_listen_port: &quot;8443&quot;
nginx_server_name: &quot;{{ dmz_fqdn }}&quot;

nginx_ssl_session_timeout: &quot;1d&quot;
nginx_ssl_session_cache: &quot;shared:SSL:50m&quot;
nginx_ssl_session_tickets: &quot;off&quot;
nginx_ssl_protocols: &quot;TLSv1.2&quot;
nginx_ssl_ciphers: &quot;ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256&quot;
nginx_ssl_prefer_server_ciphers: &quot;on&quot;
nginx_hsts_header: &quot;Strict-Transport-Security max-age=15768000&quot;

nginx_dir: &quot;/etc/nginx&quot;
nginx_conf_dir: &quot;{{nginx_dir }}/conf.d&quot;
nginx_conf: &quot;{{ nginx_dir }}/nginx.conf&quot;
nginx_apache_web_server_conf: &quot;{{ nginx_conf_dir }}/apache_web_server.conf&quot;
nginx_systemd_service: &quot;/etc/systemd/system/nginx.service&quot;

# nginx ssl using self-signed certificates
openssl_ca_private_key_passphrase: &quot;password&quot;
openssl_cipher: &quot;aes256&quot;
openssl_digest: &quot;sha256&quot;
openssl_key_size: &quot;2048&quot;
openssl_days_valid: &quot;365&quot;
openssl_subject_country: &quot;Some Country&quot;
openssl_subject_state_or_province_name: &quot;Some Province&quot;
openssl_subject_locality_name: &quot;Some Locality&quot;
openssl_subject_organization_name: &quot;Middleware Snippets&quot;
openssl_subject_organizational_unit_name: &quot;Blogging&quot;
openssl_subject_common_name: &quot;{{ ansible_fqdn }}&quot;
openssl_subject: &quot;/C={{ openssl_subject_country }}/ST={{ openssl_subject_state_or_province_name }}/L={{ openssl_subject_locality_name }}/O={{ openssl_subject_organization_name }}/OU={{ openssl_subject_organizational_unit_name }}/CN={{ openssl_subject_common_name }}&quot;
openssl_subject_client: &quot;/CN=client&quot;

openssl_certificates_directory: &quot;{{ nginx_dir }}/certificates&quot;
openssl_ca_certificates_directory: &quot;{{ openssl_certificates_directory }}/ca&quot;
openssl_server_certificates_directory: &quot;{{ openssl_certificates_directory }}/server&quot;
openssl_client_certificates_directory: &quot;{{ openssl_certificates_directory }}/client&quot;

openssl_ca_private_key_path: &quot;{{openssl_ca_certificates_directory }}/ca-key.pem&quot;
openssl_ca_certificate_path: &quot;{{openssl_ca_certificates_directory }}/ca.pem&quot;
openssl_server_private_key_path: &quot;{{ openssl_server_certificates_directory }}/server-key.pem&quot;
openssl_server_certificate_request_path: &quot;{{ openssl_server_certificates_directory }}/server.csr&quot;
openssl_server_certificate_path: &quot;{{ openssl_server_certificates_directory }}/server-cert.pem&quot;
openssl_client_private_key_path: &quot;{{ openssl_client_certificates_directory }}/key.pem&quot;
openssl_client_certificate_request_path: &quot;{{ openssl_client_certificates_directory }}/client.csr&quot;
openssl_client_extended_key_usage_path: &quot;{{ openssl_client_certificates_directory }}/extfile.cnf&quot;
openssl_client_certificate_path: &quot;{{ openssl_client_certificates_directory }}/cert.pem&quot;
&lt;/pre&gt;
    SSL configuration such as cipher suites can obtained from the &lt;a href=&quot;https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.14.2&amp;openssl=1.0.2k-fips&amp;hsts=yes&amp;profile=modern&quot; target=&quot;_blank&quot;&gt;Mozilla SSL Configuration Generator&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;Add Nginx repository.&lt;/li&gt;
    &lt;li&gt;Install Nginx.&lt;/li&gt;
    &lt;li&gt;For testing purposes generate self-signed certificates
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#!/bin/sh

echo &#39;REMOVING OLD CERTIFICATES&#39;
rm -rf {{ openssl_certificates_directory }}


echo &#39;CREATING DIRECTORIES&#39;
mkdir -p {{ openssl_ca_certificates_directory }}
mkdir -p {{ openssl_server_certificates_directory }}
mkdir -p {{ openssl_client_certificates_directory }}
 

echo &#39;GENERATING CA CERTIFICATES&#39;
# https://www.openssl.org/docs/manmaster/man1/genrsa.html
openssl genrsa -{{ openssl_cipher }} -passout pass:{{ openssl_ca_private_key_passphrase }} -out {{ openssl_ca_private_key_path }} {{ openssl_key_size }}
# https://docs.ansible.com/ansible/latest/modules/openssl_privatekey_module.html#openssl-privatekey-module
#- openssl_privatekey:
#    path: &quot;{{ openssl_ca_private_key_path }}&quot;
#    passphrase: &quot;{{ openssl_ca_private_key_passphrase }}&quot;
#    cipher: &quot;{{ openssl_cipher }}&quot;
#    size: &quot;{{ openssl_key_size }}&quot;
#    mode: 0400

# https://www.openssl.org/docs/manmaster/man1/req.html
openssl req -new -x509 -days {{ openssl_days_valid }} -passin pass:{{ openssl_ca_private_key_passphrase }} -key {{ openssl_ca_private_key_path }} -{{ openssl_digest }} -out {{ openssl_ca_certificate_path }} -subj &quot;{{ openssl_subject }}&quot;
# https://docs.ansible.com/ansible/latest/modules/openssl_csr_module.html#openssl-csr-module
#- openssl_csr:
#    path: &quot;{{ openssl_ca_certificate_path }}&quot;
#    privatekey_path: &quot;{{ openssl_ca_private_key_path }}&quot;
#    privatekey_passphrase: &quot;{{ openssl_ca_private_key_passphrase }}&quot;
#    digest: &quot;{{ openssl_digest }}&quot;
#    country_name: &quot;{{ openssl_subject_country }}&quot;
#    state_or_province_name: &quot;{{ openssl_subject_state_or_province_name }}&quot;
#    locality_name: &quot;{{ openssl_subject_locality_name }}&quot;
#    organization_name: &quot;{{ openssl_subject_organization_name }}&quot;
#    organizational_unit_name: &quot;{{ openssl_subject_organizational_unit_name }}&quot;
#    common_name: &quot;{{ openssl_subject_common_name }}&quot;
#    mode: 0444
 

echo &#39;GENERATING SERVER CERTIFICATES&#39;
# https://www.openssl.org/docs/manmaster/man1/genrsa.html
openssl genrsa -out {{ openssl_server_private_key_path }} {{ openssl_key_size }}
# https://docs.ansible.com/ansible/latest/modules/openssl_privatekey_module.html#openssl-privatekey-module
#- openssl_privatekey:
#    path: &quot;{{ openssl_server_private_key_path }}&quot;
#    cipher: &quot;{{ openssl_cipher }}&quot;
#    size: &quot;{{ openssl_key_size }}&quot;
#    mode: 0400

# https://www.openssl.org/docs/manmaster/man1/req.html
openssl req -new -key {{ openssl_server_private_key_path }} -{{ openssl_digest }} -out {{ openssl_server_certificate_request_path }} -subj &quot;{{ openssl_subject }}&quot;
# https://docs.ansible.com/ansible/latest/modules/openssl_csr_module.html#openssl-csr-module
#- openssl_csr:
#    path: &quot;{{ openssl_server_certificate_request_path }}&quot;
#    privatekey_path: &quot;{{ openssl_server_private_key_path }}&quot;
#    digest: &quot;{{ openssl_digest }}&quot;
#    country_name: &quot;{{ openssl_subject_country }}&quot;
#    state_or_province_name: &quot;{{ openssl_subject_state_or_province_name }}&quot;
#    locality_name: &quot;{{ openssl_subject_locality_name }}&quot;
#    organization_name: &quot;{{ openssl_subject_organization_name }}&quot;
#    organizational_unit_name: &quot;{{ openssl_subject_organizational_unit_name }}&quot;
#    common_name: &quot;{{ openssl_subject_common_name }}&quot;
#    mode: 0444

# https://www.openssl.org/docs/manmaster/man1/x509.html
openssl x509 -req -days {{ openssl_days_valid }} -{{ openssl_digest }} -passin pass:{{ openssl_ca_private_key_passphrase }} -in {{ openssl_server_certificate_request_path }} -CA {{ openssl_ca_certificate_path }} -CAkey {{ openssl_ca_private_key_path }} -CAcreateserial -out {{ openssl_server_certificate_path }}
# https://docs.ansible.com/ansible/latest/modules/openssl_certificate_module.html#openssl-certificate-module
#- openssl_certificate:
#    path: &quot;{{ openssl_server_certificate_path }}&quot;
#    csr_path: &quot;{{ openssl_server_certificate_request_path }}&quot;
#    ownca_path: &quot;{{ openssl_ca_certificate_path }}&quot;
#    ownca_privatekey_path: &quot;{{ openssl_ca_private_key_path }}&quot;
#    ownca_privatekey_passphrase: &quot;{{ openssl_ca_private_key_passphrase }}&quot;
#    provider: ownca
#    invalid_at: YYYYmmddHHMMSS
#    invalid_at: 20200310235959
#    mode: 0444

 
echo &#39;GENERATING CLIENT CERTIFICATES&#39;
# https://www.openssl.org/docs/manmaster/man1/genrsa.html
openssl genrsa -out {{ openssl_client_private_key_path }} {{ openssl_key_size }}
# https://www.openssl.org/docs/manmaster/man1/req.html
openssl req -new -key {{ openssl_client_private_key_path }} -{{ openssl_digest }} -out {{ openssl_client_certificate_request_path }} -subj &quot;{{ openssl_subject_client }}&quot;
# https://www.openssl.org/docs/manmaster/man1/x509.html
echo extendedKeyUsage = clientAuth &gt; {{ openssl_client_extended_key_usage_path }}
openssl x509 -req -days {{ openssl_days_valid }} -{{ openssl_digest }} -passin pass:{{ openssl_ca_private_key_passphrase }} -in {{ openssl_client_certificate_request_path }} -CA {{ openssl_ca_certificate_path }} -CAkey {{ openssl_ca_private_key_path }} -CAcreateserial -out {{ openssl_client_certificate_path }} -extfile {{ openssl_client_extended_key_usage_path }}
 

echo &#39;CHANGING PERMISSIONS&#39;
chmod 0400 {{ openssl_ca_private_key_path }} {{ openssl_server_private_key_path }} {{ openssl_client_private_key_path }}
chmod 0444 {{ openssl_ca_certificate_path }} {{ openssl_server_certificate_path }} {{ openssl_client_certificate_path }}
 

echo &#39;REMOVING CERTIFICATE SIGNING REQUESTS&#39;
rm -f {{ openssl_server_certificate_request_path }} {{ openssl_client_certificate_request_path }}
&lt;/pre&gt;
&lt;/li&gt;
    &lt;li&gt;Edit the main Nginx configuration file:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  &#39;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#39;
                      &#39;$status $body_bytes_sent &quot;$http_referer&quot; &#39;
                      &#39;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#39;;

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include {{ nginx_apache_web_server_conf }};
}
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Add the server configuration
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
map $http_upgrade $connection_upgrade {
    default upgrade;
    &#39;&#39;      close;
}

upstream apache {
    # Health-monitored upstream groups must have a zone defined
    zone apache 64k;

    # Use IP Hash for session persistence
    ip_hash;
    
    # List of Apache Web Servers
    server {{ proxy_fqdn }}:{{ apache_listen_port }} weight=100 max_fails=5 fail_timeout=300;
}

server {
    listen {{ nginx_http_listen_port }};
    server_name {{ nginx_server_name }};
    return 301 https://{{ nginx_server_name }}$request_uri;
}

server {
    listen {{ nginx_listen_port }} ssl http2;
    server_name {{ nginx_server_name }};
    
    ssl_certificate {{ openssl_server_certificate_path }};
    ssl_certificate_key {{ openssl_server_private_key_path }};
    ssl_trusted_certificate {{ openssl_ca_certificate_path }};
    ssl_session_timeout {{ nginx_ssl_session_timeout }};
    ssl_session_cache {{ nginx_ssl_session_cache }};
    ssl_session_tickets {{ nginx_ssl_session_tickets }};

    ssl_protocols {{ nginx_ssl_protocols }};
    ssl_ciphers {{ nginx_ssl_ciphers }};
    ssl_prefer_server_ciphers {{ nginx_ssl_prefer_server_ciphers }};

    # HSTS (ngx_http_headers_module is required)
    add_header {{ nginx_hsts_header }};
   
    location / {
        proxy_pass                            http://apache;
        proxy_buffering                       off;
        proxy_set_header Host                 $http_host;
        proxy_set_header X-Real-IP            $remote_addr;
        proxy_set_header X-Forwarded-For      $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto    $scheme;
        proxy_set_header WL-Proxy-SSL         true;
    }
}
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;To start NGINX using systemd, we can use
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[Unit]
Description=Nginx Web Server
After=network.target

[Service]
User=root
Group=root
Type=forking
ExecStart=/usr/sbin/nginx
ExecStop=/usr/sbin/nginx -s stop
ExecReload=/usr/sbin/nginx -s reload
PIDFile=/var/run/nginx.pid
Restart=on-failure
RestartSec=1

[Install]
WantedBy=default.target
&lt;/pre&gt;
    &lt;/li&gt;
 &lt;/ul&gt;
 
To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: add nginx stable repository
  yum_repository:
    name: &quot;{{ nginx_repo_name }}&quot;
    description: Nginx Stable Repo
    baseurl: &quot;{{ nginx_repo_baseurl }}&quot;
    gpgcheck: no
    enabled: yes

- name: install nginx
  yum:
    name: nginx
    state: present
    enablerepo: &quot;{{ nginx_repo_name }}&quot;
    
#- name: check if server certificate is valid
#  openssl_certificate:
#    path: &quot;{{ openssl_server_certificate_path }}&quot;
#    provider: assertonly
#    has_expired: false
#  ignore_errors: true
#  register: validity_check
  
- name: check if server certificate is valid
  command: /bin/sh -c &quot;openssl x509 -checkend $(( 86400 * 30 )) -in {{ openssl_server_certificate_path }}&quot;
  register: validity_check
  changed_when: false
  failed_when: &quot;&#39;Certificate will expire&#39; in validity_check.stdout or &#39;unable to load certificate&#39; in validity_check.stderr&quot;
  ignore_errors: true
  
- name: debug check if server certificate is valid
  debug: 
    var=validity_check
  changed_when: false
    
- name: edit /etc/nginx/generate_certificates.sh
  template: 
    src: roles/demo_nginx/templates/generate_certificates_sh.j2
    dest: &quot;{{ nginx_dir }}/generate_certificates.sh&quot;
    owner: root
    group: root
    mode: 0744
  register: generate_certificates_sh_created
  when: validity_check is failed
  
- name: run custom task to get a new, valid certificate in case the validity check failed
  command: /bin/sh -c &quot;{{ nginx_dir }}/generate_certificates.sh&quot;
  register: nginx_certs_changed
  when: validity_check is failed and generate_certificates_sh_created is success
  
- name: remove /etc/nginx/generate_certificates.sh
  file:
    path: &quot;{{ nginx_dir }}/generate_certificates.sh&quot;
    state: absent
  when: validity_check is failed
    
- name: edit /etc/nginx/nginx.conf
  template: 
    src: roles/demo_nginx/templates/nginx_conf.j2
    dest: &quot;{{ nginx_conf }}&quot;
    owner: root
    group: root
    mode: 0644
  register: nginx_conf_changed

- name: edit /etc/nginx/conf.d/apache_web_server.conf
  template: 
    src: roles/demo_nginx/templates/apache_web_server_conf.j2
    dest: &quot;{{ nginx_apache_web_server_conf }}&quot;
    owner: root
    group: root
    mode: 0644
  register: apache_web_server_conf_changed
  
- name: edit /etc/systemd/system/nginx.service
  template: 
    src: roles/demo_nginx/templates/nginx_service.j2
    dest: &quot;{{ nginx_systemd_service }}&quot;
    owner: root
    group: root
    mode: 0644
  register: nginx_service_changed
  
- name: start nginx service
  systemd:
    name: nginx
    state: started
    enabled: yes
  register: nginx_started
  
- name: restart nginx
  systemd:
    name: nginx
    state: restarted
  when: nginx_started is not changed and (nginx_conf_changed is changed or apache_web_server_conf_changed is changed or nginx_certs_changed is changed)
&lt;/pre&gt;

&lt;h2&gt;Load Balancer&lt;/h2&gt;
To set-up the load balancer, we use the following:

&lt;ul&gt;
    &lt;li&gt;Used variables
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
apache_dir: &quot;/etc/httpd&quot;
apache_httpd_conf_dir: &quot;{{ apache_dir }}/conf&quot;
apache_extra_conf_dir: &quot;{{ apache_dir }}/conf.d&quot;
apache_modules_conf_dir: &quot;{{ apache_dir }}/conf.modules.d&quot;

apache_httpd_conf_path: &quot;{{ apache_httpd_conf_dir }}/httpd.conf&quot;
apache_mpm_module_conf_path: &quot;{{ apache_modules_conf_dir }}/00-mpm.conf&quot;
apache_proxy_module_conf_path: &quot;{{ apache_modules_conf_dir }}/00-proxy.conf&quot;

apache_listen_port: &quot;8080&quot;
apache_server_name: &quot;{{ proxy_fqdn }}&quot;

# ServerLimit = MaxRequestWorkers / ThreadsPerChild
# MaxSpareThreads = MaxRequestWorkers
# ThreadLimit = ThreadsPerChild
# MaxConnectionsPerChild = 0
apache_mpm_server_limit: &quot;40&quot;
apache_mpm_thread_limit: &quot;25&quot;
apache_mpm_start_servers: &quot;2&quot;
apache_mpm_max_request_workers: &quot;1000&quot;
apache_mpm_max_spare_threads: &quot;1000&quot;
apache_mpm_min_spare_threads: &quot;25&quot;
apache_mpm_threads_per_child: &quot;25&quot;
apache_mpm_max_connections_per_child: &quot;0&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Install Apache Httpd.&lt;/li&gt;
    &lt;li&gt;Edit httpd.conf by setting the &lt;code&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/bind.html&quot; target=&quot;_blank&quot;&gt;Listen&lt;/a&gt;&lt;/code&gt; and the &lt;code&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/core.html#servername&quot; target=&quot;_blank&quot;&gt;ServerName&lt;/a&gt;&lt;/code&gt; directives.&lt;/li&gt;
    &lt;li&gt;Configure a &lt;a href=&quot;https://httpd.apache.org/docs/2.4/mpm.html&quot; target=&quot;_blank&quot;&gt;multi-processing module&lt;/a&gt;, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Select the MPM module which should be used by uncommenting exactly
# one of the following LoadModule lines:

# prefork MPM: Implements a non-threaded, pre-forking web server
# See: http://httpd.apache.org/docs/2.4/mod/prefork.html
#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

# worker MPM: Multi-Processing Module implementing a hybrid
# multi-threaded multi-process web server
# See: http://httpd.apache.org/docs/2.4/mod/worker.html
#
#LoadModule mpm_worker_module modules/mod_mpm_worker.so

# event MPM: A variant of the worker MPM with the goal of consuming
# threads only for connections with active processing
# See: http://httpd.apache.org/docs/2.4/mod/event.html
#
LoadModule mpm_event_module modules/mod_mpm_event.so

#
# Server-Pool Management (MPM specific)
# 


# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxRequestWorkers: maximum number of server processes allowed to start
# MaxConnectionsPerChild: maximum number of connections a server process serves
#                         before terminating
&amp;lt;IfModule mpm_prefork_module&amp;gt;
    StartServers             5
    MinSpareServers          5
    MaxSpareServers         10
    MaxRequestWorkers      250
    MaxConnectionsPerChild   0
&amp;lt;/IfModule&amp;gt;


# worker MPM
# StartServers: initial number of server processes to start
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestWorkers: maximum number of worker threads
# MaxConnectionsPerChild: maximum number of connections a server process serves before terminating

# With the following configuration, 1000 clients can connect simultaneously.
# When this is not sufficient, we must increase the MaxRequestWorkers directive. 
# When doing this, we also need to adjust the following directives 
# ServerLimit = MaxRequestWorkers / ThreadsPerChild
# MaxSpareThreads = MaxRequestWorkers
# ThreadLimit = ThreadsPerChild
# MaxConnectionsPerChild = 0
&amp;lt;IfModule mpm_worker_module&amp;gt;
    ServerLimit            {{ apache_mpm_server_limit }}
    ThreadLimit            {{ apache_mpm_thread_limit }}
    StartServers           {{ apache_mpm_start_servers }}
    MaxRequestWorkers      {{ apache_mpm_max_request_workers }}
    MaxSpareThreads        {{ apache_mpm_max_spare_threads }}
    MinSpareThreads        {{ apache_mpm_min_spare_threads }}
    ThreadsPerChild        {{ apache_mpm_threads_per_child }}
    MaxConnectionsPerChild {{ apache_mpm_max_connections_per_child }}
&amp;lt;/IfModule&amp;gt;


# event MPM
# StartServers: initial number of server processes to start
# MinSpareThreads: minimum number of worker threads which are kept spare
# MaxSpareThreads: maximum number of worker threads which are kept spare
# ThreadsPerChild: constant number of worker threads in each server process
# MaxRequestWorkers: maximum number of worker threads
# MaxConnectionsPerChild: maximum number of connections a server process serves before terminating

# With the following configuration, 1000 clients can connect simultaneously.
# When this is not sufficient, we must increase the MaxRequestWorkers directive. 
# When doing this, we also need to adjust the following directives 
# ServerLimit = MaxRequestWorkers / ThreadsPerChild
# MaxSpareThreads = MaxRequestWorkers
# ThreadLimit = ThreadsPerChild
# MaxConnectionsPerChild = 0
&amp;lt;IfModule mpm_event_module&amp;gt;
    ServerLimit            {{ apache_mpm_server_limit }}
    ThreadLimit            {{ apache_mpm_thread_limit }}
    StartServers           {{ apache_mpm_start_servers }}
    MaxRequestWorkers      {{ apache_mpm_max_request_workers }}
    MaxSpareThreads        {{ apache_mpm_max_spare_threads }}
    MinSpareThreads        {{ apache_mpm_min_spare_threads }}
    ThreadsPerChild        {{ apache_mpm_threads_per_child }}
    MaxConnectionsPerChild {{ apache_mpm_max_connections_per_child }}
&amp;lt;/IfModule&amp;gt;


# The maximum number of free Kbytes that every allocator is allowed
# to hold without calling free(). In threaded MPMs, every thread has its own
# allocator. When not set, or when set to zero, the threshold will be set to
# unlimited.
&amp;lt;IfModule !mpm_netware_module&amp;gt;
    MaxMemFree            2048
&amp;lt;/IfModule&amp;gt;
&amp;lt;IfModule mpm_netware_module&amp;gt;
    MaxMemFree             100
&amp;lt;/IfModule&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure the &lt;a href=&quot;https://httpd.apache.org/docs/2.4/mod/mod_proxy.html&quot; target=&quot;_blank&quot;&gt;mod_proxy&lt;/a&gt; module, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
LoadModule proxy_module modules/mod_proxy.so
LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_express_module modules/mod_proxy_express.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

&amp;lt;VirtualHost *:{{ apache_listen_port }}&amp;gt;

    ProxyRequests Off
    ProxyPreserveHost On
    
    ProxyPass /console http://java1.ms.com:7001/console
    ProxyPassReverse /console http://java1.ms.com:7001/console
    
    ProxyPass /consolehelp http://java1.ms.com:7001/consolehelp
    ProxyPassReverse /consolehelp http://java1.ms.com:7001/consolehelp
 
    ProxyPass /javaee7 balancer://javaee7
    &amp;lt;Proxy balancer://javaee7&amp;gt;
        BalancerMember http://java1.ms.com:9002/javaee7
        BalancerMember http://java2.ms.com:9003/javaee7
    &amp;lt;/Proxy&amp;gt;    
 
    ProxyPass /SessionTest balancer://sessiontest
    ProxyPassReverse /SessionTest balancer://sessiontest 
    Header add Set-Cookie &quot;ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/&quot; env=BALANCER_ROUTE_CHANGED
    &amp;lt;Proxy balancer://sessiontest&amp;gt;
        BalancerMember http://java1.ms.com:9002/SessionTest route=1
        BalancerMember http://java2.ms.com:9003/SessionTest route=2
        ProxySet stickysession=ROUTEID
    &amp;lt;/Proxy&amp;gt;
    
    ProxyPass /support http://test151.ms.com:80/support
    ProxyPassReverse /support http://test151.ms.com:80/support
 
    &amp;lt;Location /balancer-manager&amp;gt;
        SetHandler balancer-manager
        Order Deny,Allow
        Allow from all
    &amp;lt;/Location&amp;gt;

&amp;lt;/VirtualHost&amp;gt;
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install apache_httpd 
  yum:
    name: httpd
    state: present

- name: edit /etc/httpd/conf/httpd.conf
  template: 
    src: roles/demo_apache_httpd/templates/httpd_conf.j2
    dest: &quot;{{ apache_httpd_conf_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: httpd_conf_changed
  
- name: edit /etc/httpd/conf.modules.d/00-mpm.conf
  template: 
    src: roles/demo_apache_httpd/templates/httpd_00_mpm_conf.j2
    dest: &quot;{{ apache_mpm_module_conf_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: httpd_mpm_conf_changed
  
- name: edit /etc/httpd/conf.modules.d/00-proxy.conf
  template: 
    src: roles/demo_apache_httpd/templates/httpd_00_proxy_conf.j2
    dest: &quot;{{ apache_proxy_module_conf_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: httpd_proxy_conf_changed
    
- name: start httpd service
  systemd:
    name: httpd
    state: started
    enabled: yes
  register: httpd_started
  
- name: restart httpd
  systemd:
    name: httpd
    state: restarted
  when: httpd_started is not changed and (httpd_conf_changed is changed or httpd_mpm_conf_changed is changed or httpd_proxy_conf_changed is changed)
&lt;/pre&gt;

&lt;h2&gt;JavaEE Application Server&lt;/h2&gt;
To set-up WebLogic using Ansible can be found in various posts, such as &lt;a href=&quot;http://middlewaresnippets.blogspot.com/2015/10/weblogic-scripting-for-ansible.html&quot; target=&quot;_blank&quot;&gt;WebLogic Scripting for Ansible&lt;/a&gt; or &lt;a href=&quot;http://middlewaresnippets.blogspot.com/2016/09/working-with-configurationmanager-and.html&quot; target=&quot;_blank&quot;&gt;Working with the ConfigurationManager and DeploymentManager&lt;/a&gt;.

&lt;h2&gt;Data Server&lt;/h2&gt;

&lt;h3&gt;MySQL/MariaDB&lt;/h3&gt;
To set-up the MySQL/MariaDB database we can use the following steps:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
mariadb_repo_name: &quot;MariaDB&quot;
mariadb_repo_baseurl: &quot;http://yum.mariadb.org/10.3/centos7-amd64&quot;
mariadb_repo_gpgkey: &quot;https://yum.mariadb.org/RPM-GPG-KEY-MariaDB&quot;

mariadb_packages:
  - MariaDB-server
  - MariaDB-client

mariadb_conf_dir: &quot;/etc/my.cnf.d/&quot;
mariadb_server_conf_path: &quot;/etc/my.cnf.d/server.cnf&quot;
mariadb_server_mycnf_path: &quot;/etc/my.cnf&quot;
mariadb_client_mycnf_path: &quot;/root/.my.cnf&quot;

mariadb_anonymous_user: &quot;&quot;
mariadb_root_password: &quot;{{ overall_password }}&quot;
mariadb_root_privileges: &quot;*.*:ALL,GRANT&quot;

# example database
mariadb_example_database: &quot;exampledb&quot;
mariadb_example_user: &quot;example&quot;
mariadb_example_password: &quot;set_in_inventory&quot;
mariadb_example_privileges: &quot;{{ mariadb_example_database }}.*:ALL,GRANT&quot;
mariadb_example_url: &quot;mariadb://{{ mariadb_example_user }}:{{ mariadb_example_password }}@{{ db1_fqdn }}/{{ mariadb_example_database }}&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Add the MariaDB repository.&lt;/li&gt;
    &lt;li&gt;Install &lt;a href=&quot;https://mariadb.org/&quot; target=&quot;_blank&quot;&gt;MariaDB&lt;/a&gt; (or &lt;a href=&quot;https://www.mysql.com/&quot; target=&quot;_blank&quot;&gt;MySQL&lt;/a&gt;).&lt;/li&gt;
    &lt;li&gt;Configure the database service by using the following templates
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#
# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with &#39;.cnf&#39;, otherwise they&#39;ll be ignored.
#
[mysqld]
!includedir {{ mariadb_conf_dir }}
&lt;/pre&gt;

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#
# These groups are read by MariaDB server.
# Use it for options that only the server (but not clients) should see
#
# See the examples of server my.cnf files in /usr/share/mysql/
#

# this is read by the standalone daemon and embedded servers
[server]

# this is only for the mysqld standalone daemon
[mysqld]
bind-address = {{ db1_fqdn }}
default-storage-engine = innodb
innodb_file_per_table
collation-server = utf8_general_ci
init-connect = &#39;SET NAMES utf8&#39;
character-set-server = utf8
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_autocommit
autocommit = 0

#
# * Galera-related settings
#
[galera]
# Mandatory settings
#wsrep_on=ON
#wsrep_provider=
#wsrep_cluster_address=
#binlog_format=row
#default_storage_engine=InnoDB
#innodb_autoinc_lock_mode=2
#
# Allow server to accept connections on all interfaces.
#
#bind-address=0.0.0.0
#
# Optional setting
#wsrep_slave_threads=1
#innodb_flush_log_at_trx_commit=0

# this is only for embedded server
[embedded]

# This group is only read by MariaDB servers, not by MySQL.
# If you use the same .cnf file for MySQL and MariaDB,
# you can put MariaDB-only options here
[mariadb]

# This group is only read by MariaDB-10.3 servers.
# If you use the same .cnf file for MariaDB of different versions,
# use this group for options that older servers don&#39;t understand
[mariadb-10.3]
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Start the database service.&lt;/li&gt;
    &lt;li&gt;Secure the database service. On the command-line, we would use &lt;a href=&quot;https://dev.mysql.com/doc/refman/5.7/en/mysql-secure-installation.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;mysql_secure_installation&lt;/code&gt;&lt;/a&gt;, Ansible provides the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/mysql_db_module.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;mysql_db&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/mysql_user_module.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;mysql_user&lt;/code&gt;&lt;/a&gt; modules. Note that MySQL server installs with a default login_user of &lt;code&gt;root&lt;/code&gt; and no password. To secure this user as part of an idempotent playbook, we must create two tasks: the first must change the &lt;code&gt;root&lt;/code&gt; user&#39;s password, without providing any login_user/login_password details. The second must drop a &lt;code&gt;~/.my.cnf&lt;/code&gt; file containing the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from the file. The template (&lt;code&gt;my_cnf.j2&lt;/code&gt;) for the &lt;code&gt;~/.my.cnf&lt;/code&gt; file has the following contents
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[client]
user=root
password={{ mariadb_root_password }}
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Create an application database and user&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following: 

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: add mariadb stable repository
  yum_repository:
    name: &quot;{{ mariadb_repo_name }}&quot;
    description: MariaDB Stable Repo
    baseurl: &quot;{{ mariadb_repo_baseurl }}&quot;
    gpgcheck: yes
    enabled: yes
    gpgkey: &quot;{{ mariadb_repo_gpgkey }}&quot;

- name: install mariadb
  yum:
    name: &quot;{{ mariadb_packages }}&quot;
    state: present
    enablerepo: &quot;{{ mariadb_repo_name }}&quot;

- name: create /etc/my.cnf
  template:
    src: roles/demo_mariadb/templates/server_my_cnf.j2
    dest: &quot;{{ mariadb_server_mycnf_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: mariadb_server_mycnf_changed

- name: create /etc/my.cnf.d/server.cnf
  template:
    src: roles/demo_mariadb/templates/server_cnf.j2
    dest: &quot;{{ mariadb_server_conf_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: mariadb_server_conf_changed

- name: enable and start mariadb service
  systemd:
    name: mariadb
    state: started
    enabled: yes
  register: mariadb_started

- name: delete anonymous user
  mysql_user:
    user: &quot;{{ mariadb_anonymous_user }}&quot;
    host: &quot;{{ db1_fqdn }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;
    state: absent

- name: delete anonymous user for localhost
  mysql_user:
    user: &quot;{{ mariadb_anonymous_user }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;
    state: absent

- name: remove the test database
  mysql_db:
    db: test
    login_host: &quot;{{ db1_fqdn }}&quot;
    state: absent

- name: change root password
  mysql_user:
    name: root
    password: &quot;{{ mariadb_root_password }}&quot;
    priv: &quot;{{ mariadb_root_privileges }}&quot;
    host: &quot;{{ db1_fqdn }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;

- name: create /root/.my.cnf
  template:
    src: roles/demo_mariadb/templates/client_my_cnf.j2
    dest: &quot;{{ mariadb_client_mycnf_path }}&quot;
    owner: root
    group: root
    mode: 0600

- name: change root password
  mysql_user:
    name: root
    password: &quot;{{ mariadb_root_password }}&quot;
    priv: &quot;{{ mariadb_root_privileges }}&quot;
    host: &quot;{{ item }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;
  loop:
    - 127.0.0.1
    - ::1
    - localhost

- name: restart mariadb service
  systemd:
    name: mariadb
    state: restarted
  when: mariadb_started is success and (mariadb_server_mycnf_changed is changed or mariadb_server_conf_changed is changed)
  
- name: create application database
  mysql_db:
    login_user: root
    login_password: &quot;{{ mariadb_root_password }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;
    db: &quot;{{ mariadb_example_database }}&quot;
    state: present

- name: create user for application database
  mysql_user:
    login_user: root
    login_password: &quot;{{ mariadb_root_password }}&quot;
    login_host: &quot;{{ db1_fqdn }}&quot;
    user: &quot;{{ mariadb_example_user }}&quot;
    password: &quot;{{ mariadb_example_password }}&quot;
    priv: &quot;{{ mariadb_example_privileges }}&quot;
    state: present
    host: &quot;{{ item }}&quot;
  loop:
    - &quot;localhost&quot;
    - &quot;%&quot;
&lt;/pre&gt;

To test the set-up with the application presented &lt;a href=&quot;http://middlewaresnippets.blogspot.com/2014/07/wildflying-java-ee-7.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, we need to &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/jdbc/ConfigureDatabaseConnectivity.html&quot; target=&quot;_blank&quot;&gt;configure a data source&lt;/a&gt; in WebLogic that is either XA-enabled or using Logging Last Resource (more information can be found &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLJTA/toc.htm&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;). For the data source we can use the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[oracle@java1 jdbc]$ cat mariadb_ds-jdbc.xml
&amp;lt;jdbc-data-source ...&amp;gt;
  &amp;lt;name&amp;gt;mariadb_ds&amp;lt;/name&amp;gt;
  &amp;lt;datasource-type&amp;gt;GENERIC&amp;lt;/datasource-type&amp;gt;
  &amp;lt;jdbc-driver-params&amp;gt;
    &amp;lt;url&amp;gt;jdbc:mariadb://db1.ms.com:3306/exampledb&amp;lt;/url&amp;gt;
    &amp;lt;driver-name&amp;gt;org.mariadb.jdbc.MariaDbPoolDataSource&amp;lt;/driver-name&amp;gt;
    &amp;lt;properties&amp;gt;
      &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;user&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;example&amp;lt;/value&amp;gt;
      &amp;lt;/property&amp;gt;
    &amp;lt;/properties&amp;gt;
    &amp;lt;password-encrypted&amp;gt;{AES}Q810oPbAbLfIre6q2eCbRgy4834n3Syer05DPhnYtVI=&amp;lt;/password-encrypted&amp;gt;
  &amp;lt;/jdbc-driver-params&amp;gt;
  &amp;lt;jdbc-connection-pool-params&amp;gt;
    &amp;lt;test-table-name&amp;gt;SQL SELECT 1&amp;lt;/test-table-name&amp;gt;
  &amp;lt;/jdbc-connection-pool-params&amp;gt;
  &amp;lt;jdbc-data-source-params&amp;gt;
    &amp;lt;jndi-name&amp;gt;jdbc/exampleDS&amp;lt;/jndi-name&amp;gt;
    &amp;lt;global-transactions-protocol&amp;gt;TwoPhaseCommit&amp;lt;/global-transactions-protocol&amp;gt;
  &amp;lt;/jdbc-data-source-params&amp;gt;
&amp;lt;/jdbc-data-source&amp;gt;
&lt;/pre&gt;

Unfortunately:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
ServletContext@1777542971[app:javaee7 module:javaee7 path:null spec-version:3.1]] Servlet failed with an Exception
javax.ejb.EJBException: BEA1-0006166BC1D3178E01FA; nested exception is:
        javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.5.v20170607-b3d05bd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Unexpected exception while enlisting XAConnection java.sql.SQLException: XA error: XAResource.XAER_INVAL start() failed on resource &#39;mariadb_ds_tryout_domain&#39;: XAER_INVAL : Invalid arguments were given
javax.transaction.xa.XAException
        at org.mariadb.jdbc.MariaXaResource.mapXaException(MariaXaResource.java:120)
        at org.mariadb.jdbc.MariaXaResource.execute(MariaXaResource.java:136)
        at org.mariadb.jdbc.MariaXaResource.start(MariaXaResource.java:319)
        at weblogic.jdbc.jta.DataSource.start(DataSource.java:835)
        at weblogic.transaction.internal.XAServerResourceInfo.start(XAServerResourceInfo.java:1395)
        at weblogic.transaction.internal.XAServerResourceInfo.xaStart(XAServerResourceInfo.java:1326)
        at weblogic.transaction.internal.XAServerResourceInfo.enlist(XAServerResourceInfo.java:313)
        at weblogic.transaction.internal.ServerTransactionImpl.enlistResource(ServerTransactionImpl.java:678)
        at weblogic.transaction.internal.ServerTransactionImpl.enlistResource(ServerTransactionImpl.java:497)
        at weblogic.jdbc.jta.DataSource.enlist(DataSource.java:1778)
        at weblogic.jdbc.jta.DataSource.refreshXAConnAndEnlist(DataSource.java:1680)
        at weblogic.jdbc.jta.DataSource.getConnectionInternal(DataSource.java:520)
        at weblogic.jdbc.jta.DataSource.getConnection(DataSource.java:497)
        at weblogic.jdbc.common.internal.RmiDataSource.getConnectionInternal(RmiDataSource.java:614)
        at weblogic.jdbc.common.internal.RmiDataSource.getConnection(RmiDataSource.java:594)
        at weblogic.jdbc.common.internal.RmiDataSource.getConnection(RmiDataSource.java:587)
        at org.eclipse.persistence.sessions.JNDIConnector.connect(JNDIConnector.java:135)
        at org.eclipse.persistence.sessions.DatasourceLogin.connectToDatasource(DatasourceLogin.java:162
&lt;/pre&gt;

A bit of Searching let to &lt;a href=&quot;https://stackoverflow.com/questions/36912251/atomikos-exception-when-transaction-contains-more-than-one-persist&quot; target=&quot;_blank&quot;&gt;this&lt;/a&gt;:

&lt;ul&gt;
    &lt;li&gt;XA support is limited, see &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/xa-statements.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; and &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/xa-restrictions.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information.&lt;/li&gt;
    &lt;li&gt;Known problems with mysql &lt;a href=&quot;https://www.atomikos.com/Documentation/KnownProblems#MySQL_XA_bug&quot; target=&quot;_blank&quot;&gt;bug&lt;/a&gt;: 
&lt;blockquote cite=&quot;https://www.atomikos.com/Documentation/KnownProblems#MySQL_XA_bug&quot;&gt;
Some users have reported problems with MySQL XA (related to this MySQL &lt;a href=&quot;https://bugs.mysql.com/bug.php?id=27832external&quot; target=&quot;_blank&quot;&gt;bug&lt;/a&gt;). This problem only happens if you access the same MySQL database more than once in the same transaction. A workaround can be setting the following property in jta.properties: &lt;code&gt;com.atomikos.icatch.serial_jta_transactions=false&lt;/code&gt;. Also, make sure to set the following property on the MySQL datasource: &lt;code&gt;pinGlobalTxToPhysicalConnection=true&lt;/code&gt; MariaDB&#39;s java driver also supports this workaround since v.1.1.8.
&lt;/blockquote&gt;
    &lt;/li&gt;
&lt;/ul&gt;

As with so many workarounds sometimes they work sometimes they do not. With the &lt;code&gt;org.mariadb.jdbc.MariaDbDataSource&lt;/code&gt; (mentioned &lt;a href=&quot;https://mariadb.com/kb/en/library/about-mariadb-connector-j/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;) we run into the same problems. Note that autocommit is turned off:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
MariaDB [(none)]&gt; SHOW VARIABLES WHERE Variable_name=&#39;autocommit&#39;;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | OFF   |
+---------------+-------+
1 row in set (0.001 sec)

MariaDB [(none)]&gt; SELECT @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            0 |
+--------------+
1 row in set (0.000 sec)
&lt;/pre&gt;

Changing the driver to &lt;code&gt;org.mariadb.jdbc.Driver&lt;/code&gt; and changing the &lt;code&gt;global-transactions-protocol&lt;/code&gt; to &lt;code&gt;LoggingLastResource&lt;/code&gt; works. In the case of &lt;code&gt;LoggingLastResource&lt;/code&gt; WebLogic adds extra tables to the schema:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
MariaDB [(none)]&gt; use exampledb
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

MariaDB [exampledb]&gt; show tables;
+-----------------------------+
| Tables_in_exampledb         |
+-----------------------------+
| FILM                        |
| PERSOON                     |
| WL_LLR_APPLICATION_SERVER_1 |
| WL_LLR_APPLICATION_SERVER_2 |
+-----------------------------+
4 rows in set (0.000 sec)

MariaDB [exampledb]&gt; describe WL_LLR_APPLICATION_SERVER_1;
+-------------+---------------+------+-----+---------+-------+
| Field       | Type          | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| XIDSTR      | varchar(40)   | NO   | PRI | NULL    |       |
| POOLNAMESTR | varchar(64)   | YES  |     | NULL    |       |
| RECORDSTR   | varchar(1000) | YES  |     | NULL    |       |
+-------------+---------------+------+-----+---------+-------+
3 rows in set (0.001 sec)

MariaDB [exampledb]&gt; select * from WL_LLR_APPLICATION_SERVER_1;
+-------------------------+-------------------------+-------------------------------------+
| XIDSTR                  | POOLNAMESTR             | RECORDSTR                           |
+-------------------------+-------------------------+-------------------------------------+
| JDBC LLR Domain//Server | JDBC LLR Domain//Server | tryout_domain//application_server_1 |
| JDBC LLR Version        | JDBC LLR Version        | 1.0                                 |
+-------------------------+-------------------------+-------------------------------------+
2 rows in set (0.000 sec)
&lt;/pre&gt;

More information about the Logging Last Resource feature can be found &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLJTA/llr.htm#WLJTA216&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;:

&lt;blockquote cite=&quot;https://www.atomikos.com/Documentation/KnownProblems#MySQL_XA_bug&quot;&gt;
In a global two-phase commit transaction with a Logging Last Resource participant, the WebLogic transaction manager follows these basic steps:

&lt;ul&gt;
    &lt;li&gt;Calls prepare on all other (XA-compliant) transaction participants.&lt;/li&gt;
    &lt;li&gt;Inserts a commit record to a table on the Logging Last Resource participant (rather than to the file-based transaction log).&lt;/li&gt;
    &lt;li&gt;Commits the Logging Last Resource participant&#39;s local transaction (which includes both the transaction commit record insert and the application&#39;s SQL work).&lt;/li&gt;
    &lt;li&gt;Calls commit on all other transaction participants.&lt;/li&gt;
    &lt;li&gt;After the transaction completes successfully, lazily deletes the database transaction log entry as part of a future transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;PostgreSQL&lt;/h3&gt;
To set-up the PostgreSQL database we can use the following steps:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
postgresql_packages:
  - postgresql11
  - postgresql11-server

postgresql_bin_dir: &quot;/usr/pgsql-11/bin&quot;
postgresql_data_dir: &quot;/var/lib/pgsql/11/data&quot;
postgresql_initdb_command: &quot;{{ postgresql_bin_dir }}/postgresql-11-setup initdb&quot;
postgresql_user: &quot;postgres&quot;
postgresql_group: &quot;postgres&quot;
postgresql_listen_addresses: &quot;localhost,{{ db1_fqdn }}&quot;

postgresql_postgres_conf_path: &quot;{{ postgresql_data_dir }}/postgresql.conf&quot;
postgresql_pg_hba_conf_path: &quot;{{ postgresql_data_dir }}/pg_hba.conf&quot;

postgresql_example_database: &quot;exampledb&quot;
postgresql_example_user: &quot;example&quot;
postgresql_example_password: &quot;set_in_inventory&quot;
postgresql_example_privileges: &quot;ALL&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Add the PostgreSQL repository (which can be obtained &lt;a href=&quot;https://www.postgresql.org/download/linux/redhat/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;).&lt;/li&gt;
    &lt;li&gt;Install the PostgreSQL packages.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/11/app-initdb.html&quot; target=&quot;_blank&quot;&gt;Initialize&lt;/a&gt; the database.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/11/sql-createdatabase.html&quot; target=&quot;_blank&quot;&gt;Create&lt;/a&gt; an application database by using the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/postgresql_db_module.html#postgresql-db-module&quot; target=&quot;_blank&quot;&gt;postgresql_db&lt;/a&gt; module.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://www.postgresql.org/docs/11/sql-createuser.html&quot; target=&quot;_blank&quot;&gt;Create&lt;/a&gt; an application user by using the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/postgresql_user_module.html#postgresql-user-module&quot; target=&quot;_blank&quot;&gt;postgresql_user&lt;/a&gt; module.&lt;/li&gt;
    &lt;li&gt;Edit the PostgreSQL configuration by setting parameters, such as &lt;code&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-connection.html#GUC-LISTEN-ADDRESSES&quot; target=&quot;_blank&quot;&gt;listen_addresses&lt;/a&gt;&lt;/code&gt;, &lt;code&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-connection.html#GUC-MAX-CONNECTIONS&quot; target=&quot;_blank&quot;&gt;max_connections&lt;/a&gt;&lt;/code&gt;, and &lt;code&gt;&lt;a href=&quot;https://www.postgresql.org/docs/current/runtime-config-resource.html#GUC-MAX-PREPARED-TRANSACTIONS&quot; target=&quot;_blank&quot;&gt;max_prepared_transactions&lt;/a&gt;&lt;/code&gt;.
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
...
#------------------------------------------------------------------------------
# CONNECTIONS AND AUTHENTICATION
#------------------------------------------------------------------------------

# - Connection Settings -

listen_addresses = &#39;{{ postgresql_listen_addresses }}&#39;
...
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Edit the &lt;a href=&quot;https://www.postgresql.org/docs/current/auth-pg-hba-conf.html&quot; target=&quot;_blank&quot;&gt;pg_hba.conf&lt;/a&gt; file
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
...
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# &quot;local&quot; is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            ident
# IPv6 local connections:
host    all             all             ::1/128                 ident
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     peer
host    replication     all             127.0.0.1/32            ident
host    replication     all             ::1/128                 ident

# Allow user example from any host with IP address {{ demo_domain_ips }} to connect to database exampledb.
#
# TYPE  DATABASE        USER            ADDRESS                 METHOD
host    {{ postgresql_example_database }}       {{ postgresql_example_user }}         {{ demo_domain_ips }}          password
&lt;/pre&gt;
    &lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following: 

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install postgresql repository configuration
  yum:
    name=https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-oraclelinux11-11-2.noarch.rpm
    state=present

- name: install postgresql
  yum:
    name: &quot;{{ postgresql_packages }}&quot;
    state: present

#- name: install postgresql python library
#  pip:
#    name: psycopg2
#    state: latest
#  environment:
#    PATH: &quot;{{ ansible_env.PATH }}:{{ postgresql_bin_dir }}&quot;

- name: initialize postgresql database
  command: /bin/sh -c &quot;{{ postgresql_initdb_command }}&quot;
    creates=&quot;{{ postgresql_data_dir }}/PG_VERSION&quot;
  register: postgresql_database_initialized
  
- name: enable and start postgresql database
  systemd:
    name: postgresql-11
    state: started
    enabled: yes
  register: postgresql_started
  
- name: create application database
  become: true
  become_user: &quot;{{ postgresql_user }}&quot;
  become_method: sudo
  postgresql_db:
    name: &quot;{{ postgresql_example_database }}&quot;
    state: present

- name: create user for application database
  become: true
  become_user: &quot;{{ postgresql_user }}&quot;
  become_method: sudo
  postgresql_user:
    name: &quot;{{ postgresql_example_user }}&quot;
    password: &quot;{{ postgresql_example_password }}&quot;
    db: &quot;{{ postgresql_example_database }}&quot;
    priv: &quot;{{ postgresql_example_privileges }}&quot; 
    state: present
    
- name: edit postgresql configuration
  template:
    src: roles/demo_postgresql/templates/postgres_conf.j2
    dest: &quot;{{ postgresql_postgres_conf_path }}&quot;
    owner: &quot;{{ postgresql_user }}&quot;
    group: &quot;{{ postgresql_group }}&quot;
    mode: 0600
  register: postgresql_configuration_edited
  
- name: edit postgresql client authentication
  template:
    src: roles/demo_postgresql/templates/pg_hba_conf.j2
    dest: &quot;{{ postgresql_pg_hba_conf_path }}&quot;
    owner: &quot;{{ postgresql_user }}&quot;
    group: &quot;{{ postgresql_group }}&quot;
    mode: 0600
  register: postgresql_client_authentication_edited
  
- name: restart postgresql database
  systemd:
    name: postgresql-11
    state: restarted
  when: postgresql_configuration_edited is changed or postgresql_client_authentication_edited is changed
&lt;/pre&gt;

To test the set-up with the application presented &lt;a href=&quot;http://middlewaresnippets.blogspot.com/2014/07/wildflying-java-ee-7.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, we need to &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/jdbc/ConfigureDatabaseConnectivity.html&quot; target=&quot;_blank&quot;&gt;configure a data source&lt;/a&gt; in WebLogic that is either XA-enabled or using Logging Last Resource (more information can be found &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLJTA/toc.htm&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;). For the data source we can use the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[oracle@java1 jdbc]$ cat postgresql_ds-jdbc.xml
&amp;lt;jdbc-data-source ...&amp;gt;
  &amp;lt;name&amp;gt;postgresql_ds&amp;lt;/name&amp;gt;
  &amp;lt;datasource-type&amp;gt;GENERIC&amp;lt;/datasource-type&amp;gt;
  &amp;lt;jdbc-driver-params&amp;gt;
    &amp;lt;url&amp;gt;jdbc:postgresql://db1.ms.com:5432/exampledb&amp;lt;/url&amp;gt;
    &amp;lt;driver-name&amp;gt;org.postgresql.xa.PGXADataSource&amp;lt;/driver-name&amp;gt;
    &amp;lt;properties&amp;gt;
      &amp;lt;property&amp;gt;
        &amp;lt;name&amp;gt;user&amp;lt;/name&amp;gt;
        &amp;lt;value&amp;gt;example&amp;lt;/value&amp;gt;
      &amp;lt;/property&amp;gt;
    &amp;lt;/properties&amp;gt;
    &amp;lt;password-encrypted&amp;gt;{AES}E/473USUywhmwElhAOeSFnfccWfzH5T5+tg3ZAZolxM=&amp;lt;/password-encrypted&amp;gt;
  &amp;lt;/jdbc-driver-params&amp;gt;
  &amp;lt;jdbc-connection-pool-params&amp;gt;
    &amp;lt;test-table-name&amp;gt;SQL SELECT 1&amp;lt;/test-table-name&amp;gt;
  &amp;lt;/jdbc-connection-pool-params&amp;gt;
  &amp;lt;jdbc-data-source-params&amp;gt;
    &amp;lt;jndi-name&amp;gt;jdbc/exampleDS&amp;lt;/jndi-name&amp;gt;
    &amp;lt;global-transactions-protocol&amp;gt;TwoPhaseCommit&amp;lt;/global-transactions-protocol&amp;gt;
  &amp;lt;/jdbc-data-source-params&amp;gt;
&amp;lt;/jdbc-data-source&amp;gt;
&lt;/pre&gt;

In this case everything works as expected.
&lt;br/&gt;&lt;br/&gt;
On the other hand when using the &lt;code&gt;org.postgresql.Driver&lt;/code&gt;, with the &lt;code&gt;global-transactions-protocol&lt;/code&gt; set to &lt;code&gt;LoggingLastResource&lt;/code&gt; does not work. In this case we sometimes get

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
Exception occurred during commit of transaction Name=[EJB model.logic.CompanyBean.insertPerson(model.entities.Person)],Xid=BEA1-00021F3C2813178E01FA(433518669),Status=Rolled back. [Reason=javax.transaction.SystemException: NonXAResource &#39;WL_JDBC_LLR_POOL.postgresql_ds&#39; commit failed: javax.transaction.SystemException: weblogic.transaction.loggingresource.LoggingResourceException: org.postgresql.util.PSQLException: ERROR: value too long for type character varying(1000)],numRepliesOwedMe=0,numRepliesOwedOthers=0,seconds since begin=0,seconds left=30,useSecure=false,NonXAServerResourceInfo[WL_JDBC_LLR_POOL.postgresql_ds]=(ServerResourceInfo[WL_JDBC_LLR_POOL.postgresql_ds]=(state=rolledback,assigned=application_server_1),nonXAResource=JDBC LLR, pool=postgresql_ds, table=wl_llr_application_server_1),XAServerResourceInfo[WLStore_tryout_domain_application_cluster_filestore@application_server_2]=(ServerResourceInfo[WLStore_tryout_domain_application_cluster_filestore@application_server_2]=(state=rolledback,assigned=application_server_2),xar=null,re-Registered = false),SCInfo[tryout_domain+application_server_2]=(state=rolledback),SCInfo[tryout_domain+application_server_1]=(state=rolledback),properties=({weblogic.transaction.name=[EJB model.logic.CompanyBean.insertPerson(model.entities.Person)], weblogic.transaction.partitionName=DOMAIN, weblogic.transaction.allResourcesAssigned=true, weblogic.jdbc.remote.postgresql_ds=t3://172.20.40.144:9002, weblogic.transaction.nonXAResource=WL_JDBC_LLR_POOL.postgresql_ds, weblogic.jdbc.llr=postgresql_ds}),OwnerTransactionManager=ServerTM[ServerCoordinatorDescriptor=(CoordinatorURL=application_server_1+172.20.40.144:9002+tryout_domain+t3+ CoordinatorNonSecureURL=application_server_1+172.20.40.144:9002+tryout_domain+t3+ coordinatorSecureURL=null, XAResources={WSATGatewayRM_application_server_1_tryout_domain, WLStore_tryout_domain_application_cluster_filestore@application_server_1},NonXAResources={})],CoordinatorURL=application_server_1+172.20.40.144:9002+tryout_domain+t3+): weblogic.transaction.RollbackException: NonXAResource &#39;WL_JDBC_LLR_POOL.postgresql_ds&#39; commit failed: javax.transaction.SystemException: weblogic.transaction.loggingresource.LoggingResourceException: org.postgresql.util.PSQLException: ERROR: value too long for type character varying(1000)
        at weblogic.transaction.internal.TransactionImpl.throwRollbackException(TransactionImpl.java:2104)
        at weblogic.transaction.internal.ServerTransactionImpl.internalCommit(ServerTransactionImpl.java:376)
        at weblogic.transaction.internal.ServerTransactionImpl.commit(ServerTransactionImpl.java:262)
        at weblogic.ejb.container.internal.BaseRemoteObject.postInvoke1(BaseRemoteObject.java:442)
        at weblogic.ejb.container.internal.StatelessRemoteObject.postInvoke1(StatelessRemoteObject.java:20)
        at weblogic.ejb.container.internal.BaseRemoteObject.__WL_postInvokeTxRetry(BaseRemoteObject.java:307)
        at weblogic.ejb.container.internal.SessionRemoteMethodInvoker.invokeInternal(SessionRemoteMethodInvoker.java:67)
        at weblogic.ejb.container.internal.SessionRemoteMethodInvoker.invoke(SessionRemoteMethodInvoker.java:21)
        at model.logic.Company_hufak0_CompanyImpl.insertPerson(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at weblogic.ejb.container.internal.RemoteBusinessIntfProxy.invoke(RemoteBusinessIntfProxy.java:86)
        at com.sun.proxy.$Proxy150.insertPerson(Unknown Source)
        at userinterface.servlets.NormalServlet.doGet(NormalServlet.java:53)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
&lt;/pre&gt;

To see what is happening, we can take a look at the created tables:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
-bash-4.2$ psql -h db1.ms.com -p 5432 -U example exampledb
Password for user example:
psql (11.2)
Type &quot;help&quot; for help.

exampledb=&gt; \dt
                   List of relations
 Schema |            Name             | Type  |  Owner
--------+-----------------------------+-------+---------
 public | film                        | table | example
 public | persoon                     | table | example
 public | wl_llr_application_server_1 | table | example
 public | wl_llr_application_server_2 | table | example
(4 rows)
 
exampledb=&gt; \d+ wl_llr_application_server_1
                                   Table &quot;public.wl_llr_application_server_1&quot;
   Column    |          Type           | Collation | Nullable | Default | Storage  | Stats target | Description
-------------+-------------------------+-----------+----------+---------+----------+--------------+-------------
 xidstr      | character varying(40)   |           | not null |         | extended |              |
 poolnamestr | character varying(64)   |           |          |         | extended |              |
 recordstr   | character varying(1000) |           |          |         | extended |              |
Indexes:
    &quot;wl_llr_application_server_1_pkey&quot; PRIMARY KEY, btree (xidstr)
    
exampledb=&gt; select * from wl_llr_application_server_1;
         xidstr          |       poolnamestr       |              recordstr
-------------------------+-------------------------+-------------------------------------
 JDBC LLR Domain//Server | JDBC LLR Domain//Server | tryout_domain//application_server_1
 JDBC LLR Version        | JDBC LLR Version        | 1.0
(2 rows)

exampledb=&gt; select * from wl_llr_application_server_2;
         xidstr          |       poolnamestr       |              recordstr
-------------------------+-------------------------+-------------------------------------
 JDBC LLR Domain//Server | JDBC LLR Domain//Server | tryout_domain//application_server_2
 JDBC LLR Version        | JDBC LLR Version        | 1.0
(2 rows)
&lt;/pre&gt;

Of course, the expection - &lt;code&gt;org.postgresql.util.PSQLException: ERROR: value too long for type character varying(1000)&lt;/code&gt; - is clear and the column &lt;code&gt;recordstr&lt;/code&gt; is too small. To change this we have a look at the documentation on &lt;a href=&quot;https://www.postgresql.org/docs/current/datatype-character.html&quot; target=&quot;_blank&quot;&gt;character types&lt;/a&gt;. Let just choose &lt;code&gt;text&lt;/code&gt; as this has unlimited length (note this is just a test). To change the data type of the column &lt;code&gt;recordstr&lt;/code&gt;, we can use &lt;a href=&quot;https://www.postgresql.org/docs/current/sql-altertable.html&quot; target=&quot;_blank&quot;&gt;alter table&lt;/a&gt;

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
exampledb=&gt; ALTER TABLE wl_llr_application_server_1 ALTER COLUMN recordstr TYPE TEXT USING recordstr :: TEXT;
ALTER TABLE

exampledb=&gt; ALTER TABLE wl_llr_application_server_2 ALTER COLUMN recordstr TYPE TEXT USING recordstr :: TEXT;
ALTER TABLE

exampledb=&gt; \d+ wl_llr_application_server_1
                                  Table &quot;public.wl_llr_application_server_1&quot;
   Column    |         Type          | Collation | Nullable | Default | Storage  | Stats target | Description
-------------+-----------------------+-----------+----------+---------+----------+--------------+-------------
 xidstr      | character varying(40) |           | not null |         | extended |              |
 poolnamestr | character varying(64) |           |          |         | extended |              |
 recordstr   | text                  |           |          |         | extended |              |
Indexes:
    &quot;wl_llr_application_server_1_pkey&quot; PRIMARY KEY, btree (xidstr)
    
exampledb=&gt; select recordstr, char_length(recordstr) from wl_llr_application_server_1;
                                  recordstr                                   | char_length
------------------------------------------------------------------------------+-------------
 tryout_domain//application_server_1                                          |          35
 1.0                                                                          |           3
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|        1010
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAABnQAGXdlYmxvZ2ljLnRyYW5zYWN0aW9u+|
 Lm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbihtb2RlbC5l+|
 bnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLnBhcnRpdGlvbk5hbWV0AAZE+|
 T01BSU50ACl3ZWJsb2dpYy50cmFuc2FjdGlvbi5hbGxSZXNvdXJjZXNBc3NpZ25lZHNyABFqYXZh+|
 LmxhbmcuQm9vbGVhbs0gcoDVnPruAgABWgAFdmFsdWV4cAF0ACJ3ZWJsb2dpYy5qZGJjLnJlbW90+|
 ZS5wb3N0Z3Jlc3FsX2RzdAAXdDM6Ly8xNzIuMjAuNDAuMTQ0OjkwMDJ0ACJ3ZWJsb2dpYy50cmFu+|
 c2FjdGlvbi5ub25YQVJlc291cmNldAAeV0xfSkRCQ19MTFJfUE9PTC5wb3N0Z3Jlc3FsX2RzdAAR+|
 d2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3FsX2RzeHf+AAAACgAVHzwoExeOAfoAAAFp82z/+|
 zgAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdyZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9t+|
 YWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8yAAAA+|
 AgA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIwLjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4r+|
 dDMrADlhcHBsaWNhdGlvbl9zZXJ2ZXJfMisxNzIuMjAuNDAuMTQ2OjkwMDMrdHJ5b3V0X2RvbWFp+|
 bit0Mys=                                                                    +|
                                                                              |
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|         811
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAGdwgAAAAIAAAABXQAIndlYmxvZ2ljLmpkYmMucmVtb3Rl+|
 LnBvc3RncmVzcWxfZHN0ABd0MzovLzE3Mi4yMC40MC4xNDQ6OTAwMnQAGXdlYmxvZ2ljLnRyYW5z+|
 YWN0aW9uLm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbiht+|
 b2RlbC5lbnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLm5vblhBUmVzb3Vy+|
 Y2V0AB5XTF9KREJDX0xMUl9QT09MLnBvc3RncmVzcWxfZHN0ACJ3ZWJsb2dpYy50cmFuc2FjdGlv+|
 bi5wYXJ0aXRpb25OYW1ldAAGRE9NQUlOdAARd2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3Fs+|
 X2RzeHfDAAAACgAWHzwoExeOAfoAAAFp820DBgAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdy+|
 ZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9tYWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0+|
 b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8xAAAAAQA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIw+|
 LjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4rdDMr                                    +|
                                                                              |
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|        1010
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAABnQAGXdlYmxvZ2ljLnRyYW5zYWN0aW9u+|
 Lm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbihtb2RlbC5l+|
 bnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLnBhcnRpdGlvbk5hbWV0AAZE+|
 T01BSU50ACl3ZWJsb2dpYy50cmFuc2FjdGlvbi5hbGxSZXNvdXJjZXNBc3NpZ25lZHNyABFqYXZh+|
 LmxhbmcuQm9vbGVhbs0gcoDVnPruAgABWgAFdmFsdWV4cAF0ACJ3ZWJsb2dpYy5qZGJjLnJlbW90+|
 ZS5wb3N0Z3Jlc3FsX2RzdAAXdDM6Ly8xNzIuMjAuNDAuMTQ0OjkwMDJ0ACJ3ZWJsb2dpYy50cmFu+|
 c2FjdGlvbi5ub25YQVJlc291cmNldAAeV0xfSkRCQ19MTFJfUE9PTC5wb3N0Z3Jlc3FsX2RzdAAR+|
 d2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3FsX2RzeHf+AAAACgAZHzwoExeOAfoAAAFp820G+|
 LAAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdyZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9t+|
 YWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8yAAAA+|
 AgA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIwLjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4r+|
 dDMrADlhcHBsaWNhdGlvbl9zZXJ2ZXJfMisxNzIuMjAuNDAuMTQ2OjkwMDMrdHJ5b3V0X2RvbWFp+|
 bit0Mys=                                                                    +|
                                                                              |
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|         811
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAGdwgAAAAIAAAABXQAIndlYmxvZ2ljLmpkYmMucmVtb3Rl+|
 LnBvc3RncmVzcWxfZHN0ABd0MzovLzE3Mi4yMC40MC4xNDQ6OTAwMnQAGXdlYmxvZ2ljLnRyYW5z+|
 YWN0aW9uLm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbiht+|
 b2RlbC5lbnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLm5vblhBUmVzb3Vy+|
 Y2V0AB5XTF9KREJDX0xMUl9QT09MLnBvc3RncmVzcWxfZHN0ACJ3ZWJsb2dpYy50cmFuc2FjdGlv+|
 bi5wYXJ0aXRpb25OYW1ldAAGRE9NQUlOdAARd2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3Fs+|
 X2RzeHfDAAAACgAaHzwoExeOAfoAAAFp820JNAAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdy+|
 ZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9tYWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0+|
 b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8xAAAAAQA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIw+|
 LjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4rdDMr                                    +|
                                                                              |
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|        1010
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAABnQAGXdlYmxvZ2ljLnRyYW5zYWN0aW9u+|
 Lm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbihtb2RlbC5l+|
 bnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLnBhcnRpdGlvbk5hbWV0AAZE+|
 T01BSU50ACl3ZWJsb2dpYy50cmFuc2FjdGlvbi5hbGxSZXNvdXJjZXNBc3NpZ25lZHNyABFqYXZh+|
 LmxhbmcuQm9vbGVhbs0gcoDVnPruAgABWgAFdmFsdWV4cAF0ACJ3ZWJsb2dpYy5qZGJjLnJlbW90+|
 ZS5wb3N0Z3Jlc3FsX2RzdAAXdDM6Ly8xNzIuMjAuNDAuMTQ0OjkwMDJ0ACJ3ZWJsb2dpYy50cmFu+|
 c2FjdGlvbi5ub25YQVJlc291cmNldAAeV0xfSkRCQ19MTFJfUE9PTC5wb3N0Z3Jlc3FsX2RzdAAR+|
 d2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3FsX2RzeHf+AAAACgAcHzwoExeOAfoAAAFp820M+|
 XgAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdyZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9t+|
 YWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8yAAAA+|
 AgA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIwLjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4r+|
 dDMrADlhcHBsaWNhdGlvbl9zZXJ2ZXJfMisxNzIuMjAuNDAuMTQ2OjkwMDMrdHJ5b3V0X2RvbWFp+|
 bit0Mys=                                                                    +|
                                                                              |
 rO0ABXcIAAAAWgAAAANzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0+|         811
 b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAGdwgAAAAIAAAABXQAIndlYmxvZ2ljLmpkYmMucmVtb3Rl+|
 LnBvc3RncmVzcWxfZHN0ABd0MzovLzE3Mi4yMC40MC4xNDQ6OTAwMnQAGXdlYmxvZ2ljLnRyYW5z+|
 YWN0aW9uLm5hbWV0AEFbRUpCIG1vZGVsLmxvZ2ljLkNvbXBhbnlCZWFuLmluc2VydFBlcnNvbiht+|
 b2RlbC5lbnRpdGllcy5QZXJzb24pXXQAIndlYmxvZ2ljLnRyYW5zYWN0aW9uLm5vblhBUmVzb3Vy+|
 Y2V0AB5XTF9KREJDX0xMUl9QT09MLnBvc3RncmVzcWxfZHN0ACJ3ZWJsb2dpYy50cmFuc2FjdGlv+|
 bi5wYXJ0aXRpb25OYW1ldAAGRE9NQUlOdAARd2VibG9naWMuamRiYy5sbHJ0AA1wb3N0Z3Jlc3Fs+|
 X2RzeHfDAAAACgAeHzwoExeOAfoAAAFp820TqwAAAAIAHldMX0pEQkNfTExSX1BPT0wucG9zdGdy+|
 ZXNxbF9kcwBIV0xTdG9yZV90cnlvdXRfZG9tYWluX2FwcGxpY2F0aW9uX2NsdXN0ZXJfZmlsZXN0+|
 b3JlQGFwcGxpY2F0aW9uX3NlcnZlcl8xAAAAAQA5YXBwbGljYXRpb25fc2VydmVyXzErMTcyLjIw+|
 LjQwLjE0NDo5MDAyK3RyeW91dF9kb21haW4rdDMr                                    +|
                                                                              |
(8 rows)
&lt;/pre&gt;

Above, we see that indeed sometimes a record with a length of 1010 is inserted.

&lt;h3&gt;Directory Server&lt;/h3&gt;
 To &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/installation_guide/index&quot; target=&quot;_blank&quot;&gt;set-up the Directory Server&lt;/a&gt; we can use the following steps:
 
&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
ds_packages:
  - 389-ds-base
  - 389-admin
  #- 389-console
  - 389-ds-console
  - 389-admin-console

ds_silent_setup_path: &quot;/tmp/setup.inf&quot;
ds_nslcd_conf_path: &quot;/etc/nslcd.conf&quot;

# https://access.redhat.com/documentation/en-US/Red_Hat_Directory_Server/8.2/html/Installation_Guide/Installation_Guide-Advanced_Configuration-Silent.html
ds_full_machine_name: &quot;{{ db1_fqdn }}&quot;
ds_system_user: &quot;dirsrv&quot;
ds_system_group: &quot;dirsrv&quot;
ds_administrator_id : &quot;admin&quot;
ds_administrator_password: &quot;{{ overall_password }}&quot;
ds_administration_domain: &quot;{{ demo_domain_name }}&quot;

ds_directory_server_network_port: &quot;389&quot;
ds_directory_server_identifier: &quot;{{ db1_hostname }}&quot;
ds_suffix: &quot;dc=ms,dc=com&quot;
ds_directory_manager_dn: &quot;cn=Directory Manager&quot;
ds_directory_manager_password: &quot;{{ overall_password }}&quot;

ds_administration_port: &quot;9830&quot;

ds_ldap_authentication:
  server_uri: &quot;ldap://{{ ds_full_machine_name }}:{{ ds_directory_server_network_port }}&quot;
  bind_dn: &quot;{{ ds_directory_manager_dn }}&quot;
  bind_pw: &quot;{{ overall_password }}&quot;
  
ds_users_dn: &quot;ou=People,{{ ds_suffix }}&quot;
ds_groups_dn: &quot;ou=Groups,{{ ds_suffix }}&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Install the 389 Directory Server packages&lt;/li&gt;
    &lt;li&gt;Create setup file for the &lt;code&gt;&lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/installation_guide/index#about-setup-ds-admin_pl&quot; target=&quot;_blank&quot;&gt;setup-ds-admin.pl&lt;/a&gt;&lt;/code&gt; script
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[General] 
FullMachineName={{ ds_full_machine_name }}
SuiteSpotUserID={{ ds_system_user }}
SuiteSpotGroup={{ ds_system_group }}
ConfigDirectoryAdminID={{ ds_administrator_id }}
ConfigDirectoryAdminPwd={{ ds_administrator_password }}
AdminDomain={{ ds_administration_domain }}

[slapd] 
ServerPort={{ ds_directory_server_network_port }}
ServerIdentifier={{ ds_directory_server_identifier }}
Suffix={{ ds_suffix }}
RootDN={{ ds_directory_manager_dn }}
RootDNPwd={{ ds_directory_manager_password }}

[admin] 
Port={{ ds_administration_port }}
&lt;/pre&gt;
    More information about the silent setup can be found &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/installation_guide/index#Advanced_Configuration-Silent&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
    &lt;/li&gt;
    &lt;li&gt;Execute &lt;code&gt;setup-ds.pl&lt;/code&gt; or &lt;code&gt;setup-ds-admin.pl&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Enable and start the Directory Server and the Administration Server.&lt;/li&gt;
    &lt;li&gt;Add openssh ldap schema, such that we can add ssh keys to users, more information can be found &lt;a href=&quot;http://etcfstab.com/oraclelinux/sshkey_in_389ds.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# openssh-ldap schema for 389
dn: cn=schema
#
attributetypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13
        NAME &#39;sshPublicKey&#39;
        DESC &#39;MANDATORY: OpenSSH Public key&#39;
        EQUALITY octetStringMatch
        SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
        )
#
objectclasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0
        NAME &#39;ldapPublicKey&#39; SUP top AUXILIARY
        DESC &#39;MANDATORY: OpenSSH LPK objectclass&#39;
        MUST uid
        MAY sshPublicKey
        )
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Restart the Directory Server.&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install 389 directory server
  yum:
    name: &quot;{{ ds_packages }}&quot;
    state: present

- name: create temporary setup files
  template:
    src: roles/demo_directory_server/templates/setup_inf.j2
    dest: &quot;{{ ds_silent_setup_path }}&quot;
    owner: root
    group: root
    mode: 0644
  register: silent_setup_file_created

#- name: execute silent setup
#  command: /bin/sh -c &quot;/usr/sbin/setup-ds-admin.pl -s -f {{ ds_silent_setup_path }}&quot;
#    creates=&quot;/etc/dirsrv/slapd-{{ ds_directory_server_identifier }}/dse.ldif&quot;
#  register: executed_silent_setup
#  when: silent_setup_file_created is changed
  
- name: execute silent setup
  command: /bin/sh -c &quot;/usr/sbin/setup-ds.pl -s -f {{ ds_silent_setup_path }}&quot;
    creates=&quot;/etc/dirsrv/slapd-{{ ds_directory_server_identifier }}/dse.ldif&quot;
  register: executed_silent_setup
  when: silent_setup_file_created is changed

- name: debug silent setup
  debug:
    var=executed_silent_setup
  when: executed_silent_setup is changed

- name: delete temporary setup files that contain usernames and passwords
  file:
    path=&quot;{{ item }}&quot;
    state=absent
  loop:
    - &quot;{{ ds_silent_setup_path }}&quot;

- name: enable and start 389 directory server
  systemd:
    name: dirsrv.target
    state: started
    enabled: yes
  register: dirsrv_started
  
- name: check admin-server directory
  stat:
    path: /var/log/dirsrv/admin-serv
  register: admin_server_directory

- name: enable and start 389 administration server
  systemd:
    name: dirsrv-admin
    state: started
    enabled: yes
  when: admin_server_directory.stat.exists
  register: dirsrv_admin_started
  
- name: add openssh ldap schema
  template:
    src: roles/demo_directory_server/templates/98openssh_ldap_ldif.j2
    dest: &quot;/etc/dirsrv/slapd-{{ ds_directory_server_identifier }}/schema/98openssh_ldap.ldif&quot;
    owner: &quot;{{ ds_system_user }}&quot;
    group: &quot;{{ ds_system_group }}&quot;
    mode: 0600
  register: openssh_ldap_ldif_file_created
  
- name: restart 389 directory server
  systemd:
    name: dirsrv.target
    state: restarted
  when: openssh_ldap_ldif_file_created is changed
&lt;/pre&gt;

Note that the &lt;code&gt;&lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/installation_guide/index#Advanced_Configuration-setup-ds&quot; target=&quot;_blank&quot;&gt;setup-ds.pl&lt;/a&gt;&lt;/code&gt; command creates an instance of Directory Server without installing the Administration Server or Directory Server Console. It works exactly the same way as &lt;code&gt;setup-ds-admin.pl&lt;/code&gt;, except that the questions about the Configuration Directory Server and Administration Server are omitted. Using this command to create a Directory Server instance means that the instance has to be managed through the command line.
&lt;br/&gt;&lt;br/&gt;
When needed we can also configure &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/administration_guide/index#SecureConnections&quot; target=&quot;_blank&quot;&gt;secure connections&lt;/a&gt; for the Directory Server, to this end we can use the following steps:
&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
ds_directory_server_secure_network_port: &quot;636&quot;

ds_ldaps_authentication:
  server_uri: &quot;ldaps://{{ ds_full_machine_name }}:{{ ds_directory_server_secure_network_port }}&quot;
  bind_dn: &quot;{{ ds_directory_manager_dn }}&quot;
  bind_pw: &quot;{{ ds_directory_manager_password }}&quot;
  
ds_nss_database_password: &quot;password&quot;
ds_nss_database_path: &quot;/etc/dirsrv/slapd-{{ ds_directory_server_identifier }}&quot;
ds_nss_database_certutil_password_file_path: &quot;{{ ds_nss_database_path }}/pwdfile&quot;
ds_nss_database_password_file_path: &quot;{{ ds_nss_database_path }}/pin.txt&quot;
ds_nss_database_server_certificate_name: &quot;server-cert&quot;
ds_nss_database_ca_certificate_path: &quot;/root/{{ ds_nss_database_server_certificate_name }}.pem&quot;
ds_nss_database_fetched_ca_certificate_directory: &quot;/etc/ansible/roles/demo_directory_server_enable_tls/files/&quot;
ds_nss_database_ciphers: &quot;-all,+TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,+TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,+TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,+TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,+TLS_RSA_WITH_AES_128_CBC_SHA,+TLS_RSA_WITH_AES_128_CBC_SHA256,+TLS_RSA_WITH_AES_128_GCM_SHA256,+TLS_RSA_WITH_AES_256_CBC_SHA,+TLS_RSA_WITH_AES_256_CBC_SHA256,+TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,+TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,+TLS_RSA_WITH_SEED_CBC_SHA&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Initialize the &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/administration_guide/index#managing_the_nss_database_used_by_directory_server&quot; target=&quot;_blank&quot;&gt;NSS Database&lt;/a&gt; used by the directory server.&lt;/li&gt;
    &lt;li&gt;For testing purposes we generate a &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/administration_guide/index#generating_and_installing_a_self-signed_certificate&quot; target=&quot;_blank&quot;&gt;self-signed certificate&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/administration_guide/index#enabling_tls&quot; target=&quot;_blank&quot;&gt;Configure TLS&lt;/a&gt; in the directory server, i.e.,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
dn: cn=config
changetype: modify
replace: nsslapd-securePort
nsslapd-securePort: 636
-
replace: nsslapd-security
nsslapd-security: on
-
replace: nsslapd-ssl-check-hostname
nsslapd-ssl-check-hostname: off
-
replace: nsslapd-minssf
nsslapd-minssf: 0
-
replace: nsslapd-validate-cert
nsslapd-validate-cert: warn
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure the RSA cipher family, i.e.,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
dn: cn=RSA,cn=encryption,cn=config
cn: RSA
objectClass: top
objectClass: nsEncryptionModule
nsSSLToken: internal (software)
nsSSLPersonalitySSL: {{ ds_nss_database_server_certificate_name }}
nsSSLActivation: on
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Configure &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_directory_server/10/html-single/administration_guide/index#setting_encryption_ciphers&quot; target=&quot;_blank&quot;&gt;encryption and ciphers&lt;/a&gt;, i.e.,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
dn: cn=encryption,cn=config
changetype: modify
add: nsSSL3Ciphers
nsSSL3Ciphers: {{ ds_nss_database_ciphers }}
-
replace: sslVersionMin
sslVersionMin: TLS1.1
-
replace: sslVersionMax
sslVersionMax: TLS1.2
-
replace: nsSSLClientAuth
nsSSLClientAuth: allowed
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;With the confguration in place restart the Directory Server.&lt;/li&gt;
    &lt;li&gt;Test the set-up, to this end we edit the &lt;code&gt;/etc/openldap/ldap.conf&lt;/code&gt; file
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
#
# LDAP Defaults
#

# See ldap.conf(5) for details
# This file should be world readable but not world writable.

#BASE   dc=example,dc=com
#URI    ldap://ldap.example.com ldap://ldap-master.example.com:666

#SIZELIMIT      12
#TIMELIMIT      15
#DEREF          never

#TLS_CACERTDIR  /etc/openldap/certs

URI             {{ ds_ldap_authentication.server_uri }}
BASE            {{ ds_suffix }}
HOST            {{ ds_full_machine_name }}

#TLS_CACERTDIR   {{ ds_nss_database_path }}
TLS_CACERT     {{ ds_nss_database_ca_certificate_path }}
TLS_REQCERT     allow

# Turning this off breaks GSSAPI used with krb5 when rdns = false
SASL_NOCANON    on
&lt;/pre&gt;
    and run a &lt;code&gt;ldapsearch&lt;/code&gt; command that uses the ldaps protocol, i.e., &lt;code&gt;/usr/bin/ldapsearch -H {{ ds_ldaps_authentication.server_uri }} -D &#39;{{ ds_ldaps_authentication.bind_dn }}&#39; -w {{ ds_ldaps_authentication.bind_pw }} -x -b &#39;cn=config&#39; &#39;(objectclass=nsslapdConfig)&#39; &#39;nsslapd-security&#39;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
To combine the above in an Ansible playbook, we have the following:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# roles/demo_directory_server_enable_tls/tasks/main.yml

- name: check if nss database has been initialized
  command: /bin/sh -c &quot;/usr/bin/certutil -d {{ ds_nss_database_path }} -L&quot;
  register: database_init_check
  changed_when: false
  failed_when: &quot;&#39;Database needs user init&#39; in database_init_check.stdout&quot;
  ignore_errors: true
 
- name: debug check if nss database has been initialized
  debug:
    var=database_init_check
  changed_when: false
  
- name: generate password file for certutil
  template: 
    src: roles/demo_directory_server_enable_tls/templates/pwdfile.j2
    dest: &quot;{{ ds_nss_database_certutil_password_file_path }}&quot;
    owner: dirsrv
    group: dirsrv
    mode: 0400
  register: certutil_password_file_created
  
- name: generate password file for nss database
  template: 
    src: roles/demo_directory_server_enable_tls/templates/pin_txt.j2
    dest: &quot;{{ ds_nss_database_password_file_path }}&quot;
    owner: dirsrv
    group: dirsrv
    mode: 0400
  register: nss_database_password_file_created

- name: initialize nss database
  command: /bin/sh -c &quot;/usr/bin/certutil -d {{ ds_nss_database_path }} -f {{ ds_nss_database_certutil_password_file_path }} -N&quot;
  register: database_initialized
  when: database_init_check is failed and certutil_password_file_created is success
  
- name: edit nss database file permissions
  file:
    path: &quot;{{ ds_nss_database_path }}/{{ item }}&quot;
    owner: dirsrv
    group: dirsrv
    mode: 0600
  loop:
    - cert8.db
    - key3.db
    - secmod.db
    
- name: generate noise file
  command: /bin/sh -c &quot;openssl rand -out /root/noise.bin 4096&quot;
    creates=/root/noise.bin
  register: noise_file_generated
    
- name: check if server certificate exists
  command: /bin/sh -c &quot;/usr/bin/certutil -d {{ ds_nss_database_path }} -L&quot;
  register: server_certificate_check
  changed_when: false
  failed_when: &quot;ds_nss_database_server_certificate_name not in server_certificate_check.stdout&quot;
  ignore_errors: true
 
- name: debug check if server certificate exists
  debug:
    var=server_certificate_check
  changed_when: false

- name: create a self-signed certificate
  command: /bin/sh -c &quot;/usr/bin/certutil -S -x -d {{ ds_nss_database_path }} -f {{ ds_nss_database_certutil_password_file_path }} -z /root/noise.bin -n &#39;{{ ds_nss_database_server_certificate_name }}&#39; -s &#39;CN=$HOSTNAME&#39; -t &#39;CT,C,C&#39; -m $RANDOM --keyUsage digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment&quot;
  when: server_certificate_check is failed and noise_file_generated is success
  register: certificate_created
  
- name: export certificate
  command: /bin/sh -c &quot;certutil -d /etc/dirsrv/slapd-test151/ -L -n &#39;server-cert&#39; -a &gt; {{ ds_nss_database_ca_certificate_path }}&quot;
  register: certificate_exported
  when: certificate_created is changed
  
- name: change selinux file context for exported certificate
  command: /bin/sh -c &quot;chcon system_u:object_r:cert_t:s0 {{ ds_nss_database_ca_certificate_path }}&quot;
  when: certificate_exported is changed

- name: fetch exported certificate
  fetch:
    src: &quot;{{ ds_nss_database_ca_certificate_path }}&quot;
    dest: &quot;{{ ds_nss_database_fetched_ca_certificate_directory }}&quot;
    flat: yes
  when: certificate_exported is changed

# https://pagure.io/389-ds-base/issue/49390
#- name: enable tls
#  ldap_attr:
#    dn: &quot;cn=config&quot;
#    name: &quot;{{ item.attribute_name }}&quot;
#    values: &quot;{{ item.attribute_value }}&quot;
#    params: &quot;{{ ds_ldap_authentication }}&quot;
#  loop:
#    - { attribute_name: &#39;nsslapd-securePort&#39;, attribute_value: &#39;636&#39; }
#    - { attribute_name: &#39;nsslapd-security&#39;, attribute_value: &#39;on&#39; }
#    - { attribute_name: &#39;nsslapd-ssl-check-hostname&#39;, attribute_value: &#39;off&#39; }
#    - { attribute_name: &#39;nsslapd-minssf&#39;, attribute_value: &#39;0&#39; }
#    - { attribute_name: &#39;nsslapd-validate-cert&#39;, attribute_value: &#39;cert&#39; }
#  register: tls_enabled
  
- name: check enable tls
  command: /bin/sh -c &quot;/usr/bin/ldapsearch -H {{ ds_ldap_authentication.server_uri }} -D &#39;{{ ds_ldap_authentication.bind_dn }}&#39; -w {{ ds_ldap_authentication.bind_pw }} -x -b &#39;cn=config&#39; &#39;(objectclass=nsslapdConfig)&#39; &#39;nsslapd-security&#39;&quot;
  register: enable_tls_check
  changed_when: false
  failed_when: &quot;&#39;nsslapd-security: on&#39; not in enable_tls_check.stdout&quot;
  ignore_errors: true

- name: debug check enable tls
  debug:
    var=enable_tls_check
  changed_when: false
  
- name: create enable tls template
  template: 
    src: roles/demo_directory_server_enable_tls/templates/configure_tls_ldif.j2
    dest: &quot;/root/configure_tls.ldif&quot;
  register: enable_tls_template_created

- name: enable tls
  command: /bin/sh -c &quot;/usr/bin/ldapmodify -H {{ ds_ldap_authentication.server_uri }} -D &#39;{{ ds_ldap_authentication.bind_dn }}&#39; -w {{ ds_ldap_authentication.bind_pw }} -x -f /root/configure_tls.ldif&quot;
  when: enable_tls_check is failed and enable_tls_template_created is success
  register: tls_enabled

- name: enable the rsa cipher family
  ldap_entry:
    dn: cn=RSA,cn=encryption,cn=config
    state: present
    objectClass:
      - top
      - nsEncryptionModule
    attributes:
      cn: RSA
      nsSSLToken: internal (software)
      nsSSLPersonalitySSL: &quot;{{ ds_nss_database_server_certificate_name }}&quot;
      nsSSLActivation: on
    params: &quot;{{ ds_ldap_authentication }}&quot;
  register: rsa_cipher_family_enabled

# https://pagure.io/389-ds-base/issue/49390
#- name: edit ciphers and encryption
#  ldap_attr:
#    dn: &quot;cn=encryption,cn=config&quot;
#    name: &quot;{{ item.attribute_name }}&quot;
#    values: &quot;{{ item.attribute_value }}&quot;
#    params: &quot;{{ ds_ldap_authentication }}&quot;
#  loop:
#    - { attribute_name: &#39;nsSSL3Ciphers&#39;, attribute_value: &quot;{{ ds_nss_database_ciphers }}&quot; }
#    - { attribute_name: &#39;sslVersionMin&#39;, attribute_value: &#39;TLS1.1&#39; }
#    - { attribute_name: &#39;sslVersionMax&#39;, attribute_value: &#39;TLS1.2&#39; }
#    - { attribute_name: &#39;nsSSLClientAuth&#39;, attribute_value: &#39;allowed&#39; }
#  register: ciphers_and_encryption_edited

- name: check ciphers and encryption
  command: /bin/sh -c &quot;/usr/bin/ldapsearch -H {{ ds_ldap_authentication.server_uri }} -D &#39;{{ ds_ldap_authentication.bind_dn }}&#39; -w {{ ds_ldap_authentication.bind_pw }} -x -b &#39;cn=encryption,cn=config&#39; &#39;(objectclass=nsEncryptionConfig)&#39; &#39;nsSSL3Ciphers&#39;&quot;
  register: ciphers_and_encryption_check
  changed_when: false
  failed_when: &quot;&#39;nsSSL3Ciphers: -all&#39; not in ciphers_and_encryption_check.stdout&quot;
  ignore_errors: true

- name: debug check enable tls
  debug:
    var=ciphers_and_encryption_check
  changed_when: false
  
- name: create ciphers and encryption template
  template: 
    src: roles/demo_directory_server_enable_tls/templates/configure_encryption_ldif.j2
    dest: &quot;/root/configure_encryption.ldif&quot;
  register: ciphers_and_encryption_template_created

- name: edit ciphers and encryption
  command: /bin/sh -c &quot;/usr/bin/ldapmodify -H {{ ds_ldap_authentication.server_uri }} -D &#39;{{ ds_ldap_authentication.bind_dn }}&#39; -w {{ ds_ldap_authentication.bind_pw }} -x -f /root/configure_encryption.ldif&quot;
  when: ciphers_and_encryption_check is failed and ciphers_and_encryption_template_created is success
  register: ciphers_and_encryption_edited

- name: restart 389 directory server
  systemd:
    name: dirsrv.target
    state: restarted
  when: tls_enabled is changed or rsa_cipher_family_enabled is changed or ciphers_and_encryption_edited is changed

- name: edit /etc/openldap/ldap.conf
  template: 
    src: roles/demo_directory_server_enable_tls/templates/ldap_conf.j2
    dest: /etc/openldap/ldap.conf
    owner: root
    group: root
    mode: 0644
  register: nss_database_password_file_created
  
- name: test tls
  command: /bin/sh -c &quot;/usr/bin/ldapsearch -H {{ ds_ldaps_authentication.server_uri }} -D &#39;{{ ds_ldaps_authentication.bind_dn }}&#39; -w {{ ds_ldaps_authentication.bind_pw }} -x -b &#39;cn=config&#39; &#39;(objectclass=nsslapdConfig)&#39; &#39;nsslapd-security&#39;&quot;
  register: test_tls
  changed_when: false
  when: nss_database_password_file_created is success
  
- name: debug test tls
  debug:
    var=test_tls
  changed_when: false
  when: nss_database_password_file_created is success
&lt;/pre&gt;

&lt;strong&gt;WebLogic Authentication, Authorization and Role Mapping&lt;/strong&gt; - To test the setup let us add some users that are used by WebLogic. To this end, we add some users and groups to the Directory Server. To do this we can use either direct commands, i.e., &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/1/ldapadd&quot; target=&quot;_blank&quot;&gt;ldapadd&lt;/a&gt;&lt;/code&gt;, &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/1/ldapdelete&quot; target=&quot;_blank&quot;&gt;ldapdelete&lt;/a&gt;&lt;/code&gt;, &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/1/ldapmodify&quot; target=&quot;_blank&quot;&gt;ldapmodify&lt;/a&gt;&lt;/code&gt;, and &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/1/ldapsearch&quot; target=&quot;_blank&quot;&gt;ldapsearch&lt;/a&gt;&lt;/code&gt;, or the Ansible modules &lt;code&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/ldap_entry_module.html#ldap-entry-module&quot; target=&quot;_blank&quot;&gt;ldap_entry&lt;/a&gt;&lt;/code&gt; and &lt;code&gt;&lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/ldap_attr_module.html#ldap-attr-module&quot; target=&quot;_blank&quot;&gt;ldap_attr&lt;/a&gt;&lt;/code&gt;.
&lt;br/&gt;&lt;br/&gt;
To use the direct commands, we first need to create some ldif files, for example,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 ~]# cat groups.ldif
dn: cn=managers,ou=Groups,dc=ms,dc=com
objectClass: posixGroup
objectClass: top
objectClass: groupOfUniqueNames
cn: managers
gidNumber: 1000
description: manager group

dn: cn=employees,ou=Groups,dc=ms,dc=com
objectClass: posixGroup
objectClass: top
objectClass: groupOfUniqueNames
cn: employees
gidNumber: 1001
description: employee group

dn: cn=Deployers,ou=Groups,dc=ms,dc=com
objectClass: posixGroup
objectClass: top
objectClass: groupOfUniqueNames
cn: Deployers
gidNumber: 1002
description: Deployer Group


[root@db1 ~]# cat users.ldif
dn: cn=manager,ou=People,dc=ms,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: manager
sn: manager
uid: manager
userPassword: non_hashed_password
description: a manager

dn: cn=employee,ou=People,dc=ms,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: employee
sn: employee
uid: employee
userPassword: non_hashed_password
description: an employee

dn: cn=Deployer,ou=People,dc=ms,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Deployer
sn: Deployer
uid: Deployer
userPassword: non_hashed_password
description: A Deployer


[root@db1 ~]# cat add_users_to_groups.ldif
dn: cn=managers,ou=Groups,dc=ms,dc=com
changetype: modify
add: memberUid
memberUid: cn=manager,ou=People,dc=ms,dc=com

dn: cn=employees,ou=Groups,dc=ms,dc=com
changetype: modify
add: memberUid
memberUid: cn=employee,ou=People,dc=ms,dc=com

dn: cn=Deployers,ou=Groups,dc=ms,dc=com
changetype: modify
add: memberUid
memberUid: cn=Deployer,ou=People,dc=ms,dc=com

#dn: cn=Deployers,ou=Groups,dc=ms,dc=com
#changetype: modify
#replace: memberUid
#memberUid: cn=Deployer,ou=People,dc=ms,dc=com
&lt;/pre&gt;

Note that the passwords must not be hashed (more information can found be &lt;a href=&quot;https://directory.fedoraproject.org/docs/389ds/FAQ/faq.html#should-i-hash-passwords-in-my-ldif-files&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;):

&lt;blockquote cite=&quot;https://directory.fedoraproject.org/docs/389ds/FAQ/faq.html#should-i-hash-passwords-in-my-ldif-files&quot;&gt;
Should I hash passwords in my LDIF files? No! You must always submit a clear text password to the directory server in an ldap add or modify request. &quot;System&quot; passwords like nsmultiplexorcredentials for example will be base64 encoded when viewed in the dse.ldif file. Any entry in the dse.ldif file that contains a :: after the name is base64 encoded. All &quot;system&quot; passwords will use the same level of encryption and this level is not changeable. User password encryption level can be changed in the 389-console or by using:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
ldapmodify -x -D &quot;cn=directory manager&quot; -w password
dn: cn=config
changetype: modify
replace: passwordStorageScheme
passwordStorageScheme: &amp;lt;encryption scheme here&amp;gt;
&lt;/pre&gt;
&lt;/blockquote&gt;

Initially, the Directory Server is set-up with some groups that we want to get rid of

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 ~]# ldapdelete -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389
Enter LDAP Password:
cn=Accounting Managers,ou=Groups,dc=ms,dc=com
cn=HR Managers,ou=Groups,dc=ms,dc=com
cn=QA Managers,ou=Groups,dc=ms,dc=com
cn=PD Managers,ou=Groups,dc=ms,dc=com
^C
&lt;/pre&gt;

such that we are left with

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 ~]# ldapsearch -x -b &quot;dc=ms, dc=com&quot;
# extended LDIF
#
# LDAPv3
# base &amp;lt;dc=ms, dc=com&amp;gt; with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# ms.com
dn: dc=ms,dc=com
objectClass: top
objectClass: domain
dc: ms

# Directory Administrators, ms.com
dn: cn=Directory Administrators,dc=ms,dc=com
objectClass: top
objectClass: groupofuniquenames
cn: Directory Administrators
uniqueMember: cn=Directory Manager

# Groups, ms.com
dn: ou=Groups,dc=ms,dc=com
objectClass: top
objectClass: organizationalunit
ou: Groups

# People, ms.com
dn: ou=People,dc=ms,dc=com
objectClass: top
objectClass: organizationalunit
ou: People

# Special Users, ms.com
dn: ou=Special Users,dc=ms,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Special Users
description: Special Administrative Accounts

# search result
search: 2
result: 0 Success

# numResponses: 6
# numEntries: 5
&lt;/pre&gt;

To add our users and groups we execute the following commands:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 ~]# ldapadd -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389 -x -f groups.ldif
Enter LDAP Password:
adding new entry &quot;cn=managers,ou=Groups,dc=ms,dc=com&quot;
adding new entry &quot;cn=employees,ou=Groups,dc=ms,dc=com&quot;
adding new entry &quot;cn=Deployers,ou=Groups,dc=ms,dc=com&quot;

[root@db1 ~]# ldapadd -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389 -x -f users.ldif
Enter LDAP Password:
adding new entry &quot;cn=manager,ou=People,dc=ms,dc=com&quot;
adding new entry &quot;cn=employee,ou=People,dc=ms,dc=com&quot;
adding new entry &quot;cn=Deployer,ou=People,dc=ms,dc=com&quot;

[root@db1 ~]# ldapmodify -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389 -x -f add_users_to_groups.ldif
Enter LDAP Password:
modifying entry &quot;cn=managers,ou=Groups,dc=ms,dc=com&quot;
modifying entry &quot;cn=employees,ou=Groups,dc=ms,dc=com&quot;
modifying entry &quot;cn=Deployers,ou=Groups,dc=ms,dc=com&quot;

[root@db1 ~]# ldapsearch -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389 -b &quot;cn=managers,ou=Groups,dc=ms,dc=com&quot;
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base &amp;lt;cn=managers,ou=Groups,dc=ms,dc=com&amp;gt; with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# managers, Groups, ms.com
dn: cn=managers,ou=Groups,dc=ms,dc=com
cn: managers
objectClass: posixGroup
objectClass: top
objectClass: groupOfUniqueNames
memberUid: cn=manager,ou=People,dc=ms,dc=com
gidNumber: 1000
description: managers group

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

[root@db1 ~]# ldapsearch -D &quot;cn=directory manager&quot; -W -h db1.ms.com -p 389 -b &quot;cn=manager,ou=People,dc=ms,dc=com&quot;
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base &amp;lt;cn=manager,ou=People,dc=ms,dc=com&amp;gt; with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# manager, People, ms.com
dn: cn=manager,ou=People,dc=ms,dc=com
description: a manager
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
sn: manager
uid: manager
cn: manager
userPassword:: e1NTSEE1MTJ9MTczSnFjY0NqUWVMRTQrRitxM0pEc0hsOHhveTRjaWNNSUNnZnF
 Dc2ZZQ2krYnpoYTRVQkdhS3JrN1NYRWV0NHh5Mjg5UUZCUHpZcWhwRC9qQWlPREVBazFma1hxTDdy

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
&lt;/pre&gt;

With the groups and users in place, we continue with configuring WebLogic (how to configure an Authentication Provider is descibed &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/security/ConfigureAuthenticationProviders.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;):

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
WebLogic Config:
----- Common
Control Flag: SUFFICIENT
----- Provider Specific
--- Connnection
Host: db1.ms.com
Port: 389
Principal: cn=Directory Manager
Credential: password
Confirm Credential: password
--- Users
User Base DN: ou=people, dc=ms, dc=com
All Users Filter: (&amp;(cn=*)(objectclass=person))
User From Name Filter: (&amp;(cn=%u)(objectclass=person))
User Search Scope: subtree
User Name Attribute: cn
User Object Class: person
Use Retrieved User Name as Principal: checked
--- Groups
Group Base DN: ou=groups, dc=ms, dc=com
All Groups Filter: (&amp;(cn=*)(objectclass=groupOfUniqueNames))
Group From Name Filter: (&amp;(cn=%g)(objectclass=groupOfUniqueNames))
Group Search Scope: subtree
Group Membership Searching: unlimited
Max Group Membership Search Level: 0
--- Static Groups
Static Group Name Attribute: cn
Static Group Object Class: groupOfUniqueNames
Static Member DN Attribute: memberUid
Static Group DNs from Member DN Filter: (&amp;(memberUid=%M)(objectclass=groupOfUniqueNames))
&lt;/pre&gt;

If needed we can &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/security/ReorderAuthenticationProviders.html&quot; target=&quot;_blank&quot;&gt;re-order&lt;/a&gt; the authentication providers. On the &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/security/ManageUsersAndGroups.html&quot; target=&quot;_blank&quot;&gt;Users and Groups&lt;/a&gt; tab, we can check if the configuration is correct.
&lt;br/&gt;&lt;br/&gt;
To test the setup create a JavaEE application that has a security config such as:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# web.xml
&amp;lt;web-app ...&amp;gt;
    &amp;lt;servlet&amp;gt;
        &amp;lt;servlet-name&amp;gt;TestServlet&amp;lt;/servlet-name&amp;gt;
        &amp;lt;servlet-class&amp;gt;userinterface.servlets.TestServlet&amp;lt;/servlet-class&amp;gt;
    &amp;lt;/servlet&amp;gt;
    &amp;lt;servlet-mapping&amp;gt;
        &amp;lt;servlet-name&amp;gt;TestServlet&amp;lt;/servlet-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;/test&amp;lt;/url-pattern&amp;gt;
    &amp;lt;/servlet-mapping&amp;gt;
    &amp;lt;security-constraint&amp;gt;
        &amp;lt;web-resource-collection&amp;gt;
            &amp;lt;web-resource-name&amp;gt;All&amp;lt;/web-resource-name&amp;gt;
            &amp;lt;url-pattern&amp;gt;/test&amp;lt;/url-pattern&amp;gt;
        &amp;lt;/web-resource-collection&amp;gt;
        &amp;lt;auth-constraint&amp;gt;
            &amp;lt;role-name&amp;gt;MANAGER&amp;lt;/role-name&amp;gt;
            &amp;lt;role-name&amp;gt;EMPLOYEE&amp;lt;/role-name&amp;gt;
        &amp;lt;/auth-constraint&amp;gt;
        &amp;lt;user-data-constraint&amp;gt;
            &amp;lt;transport-guarantee&amp;gt;NONE&amp;lt;/transport-guarantee&amp;gt;
        &amp;lt;/user-data-constraint&amp;gt;
    &amp;lt;/security-constraint&amp;gt;
    &amp;lt;login-config&amp;gt;
        &amp;lt;auth-method&amp;gt;FORM&amp;lt;/auth-method&amp;gt;
        &amp;lt;form-login-config&amp;gt;
            &amp;lt;form-login-page&amp;gt;/login.jsp&amp;lt;/form-login-page&amp;gt;
            &amp;lt;form-error-page&amp;gt;/login.jsp&amp;lt;/form-error-page&amp;gt;
        &amp;lt;/form-login-config&amp;gt;
    &amp;lt;/login-config&amp;gt;
    &amp;lt;security-role&amp;gt;
        &amp;lt;role-name&amp;gt;MANAGER&amp;lt;/role-name&amp;gt;
    &amp;lt;/security-role&amp;gt;
    &amp;lt;security-role&amp;gt;
        &amp;lt;role-name&amp;gt;EMPLOYEE&amp;lt;/role-name&amp;gt;
    &amp;lt;/security-role&amp;gt;
&amp;lt;/web-app&amp;gt;

# weblogic.xml
&amp;lt;weblogic-web-app ...&amp;gt;
  &amp;lt;security-role-assignment&amp;gt;
    &amp;lt;role-name&amp;gt;EMPLOYEE&amp;lt;/role-name&amp;gt;
    &amp;lt;principal-name&amp;gt;employees&amp;lt;/principal-name&amp;gt;
  &amp;lt;/security-role-assignment&amp;gt;
  &amp;lt;security-role-assignment&amp;gt;
    &amp;lt;role-name&amp;gt;MANAGER&amp;lt;/role-name&amp;gt;
    &amp;lt;principal-name&amp;gt;managers&amp;lt;/principal-name&amp;gt;
  &amp;lt;/security-role-assignment&amp;gt;
  &amp;lt;session-descriptor&amp;gt;
    &amp;lt;timeout-secs&amp;gt;120&amp;lt;/timeout-secs&amp;gt;
    &amp;lt;invalidation-interval-secs&amp;gt;30&amp;lt;/invalidation-interval-secs&amp;gt;
    &amp;lt;persistent-store-type&amp;gt;replicated_if_clustered&amp;lt;/persistent-store-type&amp;gt;
  &amp;lt;/session-descriptor&amp;gt;
&amp;lt;/weblogic-web-app&amp;gt;
&lt;/pre&gt;

or simply login to WebLogic Admin Console using the &lt;code&gt;Deployer&lt;/code&gt; user. By tailing the Directory Server&#39;s access log we can see what is going on:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 ~]# tail -f /var/log/dirsrv/slapd-db1/access
[07/Apr/2019:04:50:15.998788675 -0400] conn=73 op=4 SRCH base=&quot;ou=people,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(cn=manager)(objectClass=person))&quot; attrs=&quot;1.1&quot;
[07/Apr/2019:04:50:15.998997938 -0400] conn=73 op=4 RESULT err=0 tag=101 nentries=1 etime=0.0000285909
[07/Apr/2019:04:50:15.999820665 -0400] conn=78 fd=74 slot=74 connection from 172.20.40.144 to 172.20.40.145
[07/Apr/2019:04:50:16.000158555 -0400] conn=78 op=0 BIND dn=&quot;cn=Directory Manager&quot; method=128 version=3
[07/Apr/2019:04:50:16.000208944 -0400] conn=78 op=0 RESULT err=0 tag=97 nentries=0 etime=0.0000088098 dn=&quot;cn=directory manager&quot;
[07/Apr/2019:04:50:16.000435945 -0400] conn=78 op=1 BIND dn=&quot;cn=manager,ou=People,dc=ms,dc=com&quot; method=128 version=3
[07/Apr/2019:04:50:16.000595855 -0400] conn=78 op=1 RESULT err=0 tag=97 nentries=0 etime=0.0000186900 dn=&quot;cn=manager,ou=people,dc=ms,dc=com&quot;
[07/Apr/2019:04:50:16.001756346 -0400] conn=73 op=5 SRCH base=&quot;ou=groups,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(memberUid=cn=manager,ou=People,dc=ms,dc=com)(objectClass=groupOfUniqueNames))&quot; attrs=&quot;cn&quot;
[07/Apr/2019:04:50:16.001887439 -0400] conn=73 op=5 RESULT err=0 tag=101 nentries=1 etime=0.0002866443 notes=U
[07/Apr/2019:04:50:16.002557064 -0400] conn=73 op=6 SRCH base=&quot;ou=groups,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(memberUid=cn=managers,ou=Groups,dc=ms,dc=com)(objectClass=groupOfUniqueNames))&quot; attrs=&quot;cn&quot;
[07/Apr/2019:04:50:16.002616354 -0400] conn=73 op=6 RESULT err=0 tag=101 nentries=0 etime=0.0000711509 notes=U
[07/Apr/2019:04:50:58.697434110 -0400] conn=71 op=10 SRCH base=&quot;ou=people,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(cn=Deployer)(objectClass=person))&quot; attrs=&quot;1.1&quot;
[07/Apr/2019:04:50:58.697654188 -0400] conn=71 op=10 RESULT err=0 tag=101 nentries=1 etime=0.0000309407
[07/Apr/2019:04:50:58.698351383 -0400] conn=79 fd=75 slot=75 connection from 172.20.40.144 to 172.20.40.145
[07/Apr/2019:04:50:58.699618050 -0400] conn=79 op=0 BIND dn=&quot;cn=Directory Manager&quot; method=128 version=3
[07/Apr/2019:04:50:58.699694505 -0400] conn=79 op=0 RESULT err=0 tag=97 nentries=0 etime=0.0000128638 dn=&quot;cn=directory manager&quot;
[07/Apr/2019:04:50:58.700144973 -0400] conn=79 op=1 BIND dn=&quot;cn=Deployer,ou=People,dc=ms,dc=com&quot; method=128 version=3
[07/Apr/2019:04:50:58.700284861 -0400] conn=79 op=1 RESULT err=0 tag=97 nentries=0 etime=0.0000161073 dn=&quot;cn=deployer,ou=people,dc=ms,dc=com&quot;
[07/Apr/2019:04:50:58.701065738 -0400] conn=71 op=11 SRCH base=&quot;ou=groups,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(memberUid=cn=Deployer,ou=People,dc=ms,dc=com)(objectClass=groupOfUniqueNames))&quot; attrs=&quot;cn&quot;
[07/Apr/2019:04:50:58.701174696 -0400] conn=71 op=11 RESULT err=0 tag=101 nentries=1 etime=0.0003450597 notes=U
[07/Apr/2019:04:50:58.701733032 -0400] conn=71 op=12 SRCH base=&quot;ou=groups,dc=ms,dc=com&quot; scope=2 filter=&quot;(&amp;(memberUid=cn=Deployers,ou=Groups,dc=ms,dc=com)(objectClass=groupOfUniqueNames))&quot; attrs=&quot;cn&quot;
[07/Apr/2019:04:50:58.701797526 -0400] conn=71 op=12 RESULT err=0 tag=101 nentries=0 etime=0.0000597109 notes=U
&lt;/pre&gt;

More information about security in WebLogic can be found &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/SECMG/toc.htm&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
To add groups and users using Ansible, we can use the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# variables
ds_managers_group: &quot;managers&quot;
ds_managers_group_id: &quot;1000&quot;
ds_manager_group_members:
  - &quot;manager&quot;
ds_employees_group: &quot;employees&quot;
ds_employees_group_id: &quot;1001&quot;
ds_employees_group_members:
  - &quot;employee&quot;
ds_deployers_group: &quot;Deployers&quot;
ds_deployers_group_id: &quot;1002&quot;
ds_deployers_group_members:
  - &quot;Deployer&quot;

ds_weblogic_groups:
  - {
      cn: &quot;{{ ds_managers_group }}&quot;,
      gidNumber: &quot;{{ ds_managers_group_id }}&quot;,
      description: &#39;A group for managers&#39;
    }
  - {
      cn: &quot;{{ ds_employees_group }}&quot;,
      gidNumber: &quot;{{ ds_employees_group_id }}&quot;,
      description: &#39;A group for employees&#39;
    }
  - {
      cn: &quot;{{ ds_deployers_group }}&quot;,
      gidNumber: &quot;{{ ds_deployers_group_id }}&quot;,
      description: &#39;Deployers can view all resource attributes and deploy applications.&#39;
    }

ds_weblogic_users:
  - {
      cn: &#39;manager&#39;,
      sn: &#39;manager&#39;,
      uid: &#39;manager&#39;,
      userPassword: &#39;non_hashed_password&#39;,
      description: &#39;A manager&#39;
    }
  - {
      cn: &#39;employee&#39;,
      sn: &#39;employee&#39;,
      uid: &#39;employee&#39;,
      userPassword: &#39;non_hashed_password&#39;,
      description: &#39;An employee&#39;
    }
  - {
      cn: &#39;Deployer&#39;,
      sn: &#39;Deployer&#39;,
      uid: &#39;Deployer&#39;,
      userPassword: &#39;non_hashed_password&#39;,
      description: &#39;A Deployer&#39;
    }

# tasks
- name: remove existing groups
  ldap_entry:
    dn: &quot;{{ item }}&quot;
    state: absent
    params: &quot;{{ ds_ldap_authentication }}&quot;
  loop:
    - &quot;cn=Accounting Managers,ou=Groups,{{ ds_suffix }}&quot;
    - &quot;cn=HR Managers,ou=Groups,{{ ds_suffix }}&quot;
    - &quot;cn=QA Managers,ou=Groups,{{ ds_suffix }}&quot;
    - &quot;cn=PD Managers,ou=Groups,{{ ds_suffix }}&quot;

- name: create groups
  ldap_entry:
    dn: &quot;cn={{ item.cn }},{{ ds_groups_dn }}&quot;
    state: present
    objectClass:
      - posixGroup
      - top
      - groupOfUniqueNames
    attributes:
      cn: &quot;{{ item.cn }}&quot;
      gidNumber: &quot;{{ item.gidNumber }}&quot;
      description: &quot;{{ item.description }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  loop: &quot;{{ ds_weblogic_groups }}&quot;
    
- name: create users
  ldap_entry:
    dn: &quot;cn={{ item.cn }},{{ ds_users_dn }}&quot;
    state: present
    objectClass:
      - top
      - person
      - organizationalPerson
      - inetOrgPerson
    attributes:
      cn: &quot;{{ item.cn }}&quot;
      sn: &quot;{{ item.sn }}&quot;
      uid: &quot;{{ item.uid }}&quot;
      userPassword: &quot;{{ item.userPassword }}&quot;
      description: &quot;{{ item.description }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  loop: &quot;{{ ds_weblogic_users }}&quot;

- name: users in managers group
  ldap_attr:
    dn: &quot;cn={{ ds_managers_group }},{{ ds_groups_dn }}&quot;
    state: present
    name: memberUid
    values: &quot;cn={{ item }},{{ ds_users_dn }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  when: not( (item is undefined) or (item is none) or (item | trim==&#39;&#39;) )
  loop: &quot;{{ ds_manager_group_members }}&quot;

- name: users in employees group
  ldap_attr:
    dn: &quot;cn={{ ds_employees_group }},{{ ds_groups_dn }}&quot;
    state: present
    name: memberUid
    values: &quot;cn={{ item }},{{ ds_users_dn }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  when: not( (item is undefined) or (item is none) or (item | trim==&#39;&#39;) )
  loop: &quot;{{ ds_employees_group_members }}&quot;

- name: users in deployers group
  ldap_attr:
    dn: &quot;cn={{ ds_deployers_group }},{{ ds_groups_dn }}&quot;
    state: present
    name: memberUid
    values: &quot;cn={{ item }},{{ ds_users_dn }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  when: not( (item is undefined) or (item is none) or (item | trim==&#39;&#39;) )
  loop: &quot;{{ ds_deployers_group_members }}&quot;
&lt;/pre&gt;

&lt;strong&gt;Linux Authentication, Authorization and Role Mapping&lt;/strong&gt; - To use an the Directory Server for identity and authentication management on Linux systems, we can use &lt;a href=&quot;https://arthurdejong.org/nss-pam-ldapd/&quot; target=&quot;_blank&quot;&gt;nss-pam-ldap&lt;/a&gt;:

&lt;blockquote cite=&quot;https://arthurdejong.org/nss-pam-ldapd/&quot;&gt;
This is nss-pam-ldapd which provides a Name Service Switch (NSS, nsswitch) module that allows your LDAP server to provide user account, group, host name, alias, netgroup, and basically any other information that you would normally get from /etc flat files or NIS. It also provides a Pluggable Authentication Module (PAM) to do identity and authentication management with an LDAP server on unix systems.
&lt;br/&gt;&lt;br/&gt;
This is implemented using thin NSS and PAM modules which delegate to a dedicated service (nslcd) that queries the LDAP server with persistent connections, authentication, attribute translation, etc.
&lt;br/&gt;&lt;br/&gt;
The NSS module was originally a fork of nss_ldap with some structural design improvements. The most important features of nss-pam-ldapd are:

&lt;ul&gt;
    &lt;li&gt;light and simple NSS and PAM libraries&lt;/li&gt;
    &lt;li&gt;avoid loading LDAP and SSL libraries in all programs&lt;/li&gt;
    &lt;li&gt;separation between NSS, PAM and LDAP code&lt;/li&gt;
    &lt;li&gt;fewer connections to the LDAP server&lt;/li&gt;
    &lt;li&gt;better debugging possibilities&lt;/li&gt;
    &lt;li&gt;better performance&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

To set-up nss-pam-ldap we can use the following steps:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
nss_pam_ldap_packages:
  - nss-pam-ldapd
  - nscd
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Install the nss-pam-ldap packages.&lt;/li&gt;
    &lt;li&gt;Enable and start the &lt;code&gt;nslcd&lt;/code&gt; service.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://arthurdejong.org/nss-pam-ldapd/nslcd.conf.5&quot; target=&quot;_blank&quot;&gt;Configure&lt;/a&gt; nss-pam-ldap.
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# This is the configuration file for the LDAP nameservice
# switch library&#39;s nslcd daemon. It configures the mapping
# between NSS names (see /etc/nsswitch.conf) and LDAP
# information in the directory.
# See the manual page nslcd.conf(5) for more information.

# The user and group nslcd should run as.
uid nslcd
gid ldap

# The uri pointing to the LDAP server to use for name lookups.
# Multiple entries may be specified. The address that is used
# here should be resolvable without using LDAP (obviously).
#uri ldap://127.0.0.1/
#uri ldaps://127.0.0.1/
#uri ldapi://%2fvar%2frun%2fldapi_sock/
# Note: %2f encodes the &#39;/&#39; used as directory separator
uri ldap://{{ db1_fqdn }}:{{ ds_directory_server_network_port }}

# The LDAP version to use (defaults to 3
# if supported by client library)
#ldap_version 3

# The distinguished name of the search base.
base {{ ds_suffix }}

# The distinguished name to bind to the server with.
# Optional: default is to bind anonymously.
binddn {{ ds_directory_manager_dn }}

# The credentials to bind with.
# Optional: default is no credentials.
# Note that if you set a bindpw you should check the permissions of this file.
bindpw {{ ds_directory_manager_password }}

# The distinguished name to perform password modifications by root by.
#rootpwmoddn cn=admin,dc=example,dc=com

# The default search scope.
#scope sub
#scope one
#scope base

# Customize certain database lookups.
base   group  {{ ds_groups_dn }}
base   passwd {{ ds_users_dn }}
base   shadow {{ ds_users_dn }}
#scope  group  onelevel
#scope  hosts  sub

# Bind/connect timelimit.
bind_timelimit 30

# Search timelimit.
timelimit 30

# Idle timelimit. nslcd will close connections if the
# server has not been contacted for the number of seconds.
#idle_timelimit 3600

# Use StartTLS without verifying the server certificate.
ssl no
...
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Restart &lt;code&gt;nslcd&lt;/code&gt; service.&lt;/li&gt;
    &lt;li&gt;Configure user information service in &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt; and authentication functions in &lt;code&gt;/etc/pam.d/system-auth&lt;/code&gt; and &lt;code&gt;/etc/pam.d/password-auth&lt;/code&gt; by using the authentication configuration tool &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/8/authconfig&quot; target=&quot;_blank&quot;&gt;authconfig&lt;/a&gt;&lt;/code&gt;. For more information see the &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index&quot; target=&quot;_blank&quot;&gt;System-Level Authentication Guide&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Enable &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#authconfig-homedirs&quot; target=&quot;_blank&quot;&gt;custom home directories&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install nss-pam-ldap (nslcd)
  yum:
    name: &quot;{{ nss_pam_ldap_packages }}&quot;
    state: present
    
- name: enable and start nslcd service
  systemd:
    name: nslcd
    state: started
    enabled: yes
  register: nslcd_started

- name: configure nss-pam-ldap (nslcd)
  template:
    src: roles/demo_nss_pam_ldap/templates/nslcd_conf.j2
    dest: &quot;{{ nss_pam_ldap_conf_path }}&quot;
    owner: root
    group: root
    mode: 0600
  register: nslcd_configured

- name: restart nslcd service
  systemd:
    name: nslcd
    state: restarted
  when: nslcd_configured is changed

- name: check if authconfig ldap is enabled
  lineinfile:
    path: /etc/nsswitch.conf
    line: &quot;passwd:     files sss ldap&quot;
    state: present
  check_mode: yes
  register: authconfig_ldap_enabled

- name: debug authconfig
  debug:
    var=authconfig_ldap_enabled
  when: authconfig_ldap_enabled is changed

- name: execute authconfig enable ldap
  command: /bin/sh -c &quot;/sbin/authconfig --update --enableldap --enableldapauth&quot;
  when: authconfig_ldap_enabled is changed

- name: check if authconfig mkhomedir is enabled
  lineinfile:
    path: /etc/pam.d/system-auth-ac
    line: &quot;session     optional      pam_mkhomedir.so umask=0077&quot;
    state: present
  check_mode: yes
  register: authconfig_mkhomedir_enabled

- name: debug authconfig
  debug:
    var=authconfig_mkhomedir_enabled
  when: authconfig_mkhomedir_enabled is changed

- name: execute authconfig enable mkhomedir
  command: /bin/sh -c &quot;/sbin/authconfig --update --enablemkhomedir&quot;
  when: authconfig_mkhomedir_enabled is changed
&lt;/pre&gt;

To test the set-up, we add a (Linux) user and group to the Directory Server

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# variables
ds_operator_group: &quot;operators&quot;
ds_operator_group_id: &quot;5000&quot;
    
ds_linux_groups:
  - {
      cn: &quot;{{ ds_operator_group }}&quot;,
      gidNumber: &quot;{{ ds_operator_group_id }}&quot;,
      description: &#39;Group for operators&#39;
    }
    
ds_linux_users:
  - {
      cn: &#39;FirstName&#39;,
      sn: &#39;LastName&#39;,
      uid: &#39;testuser&#39;,
      userPassword: &#39;non_hashed_password&#39;,
      uidNumber: &#39;10000&#39;,
      gidNumber: &quot;{{ ds_operator_group_id }}&quot;,
      homeDirectory: &#39;/home/testuser&#39;,
      loginShell: &#39;/bin/bash&#39;,
      gecos: &#39;FullName&#39;,
      sshPublicKey: &#39;ssh-rsa AAAAB3NzaC1yc2EA ... BA5X+SZ77dUUDWKv&#39;
    }

# tasks
- name: create linux groups
  ldap_entry:
    dn: &quot;cn={{ item.cn }},{{ ds_groups_dn }}&quot;
    state: present
    objectClass:
      - posixGroup
    attributes:
      cn: &quot;{{ item.cn }}&quot;
      gidNumber: &quot;{{ item.gidNumber }}&quot;
      description: &quot;{{ item.description }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  loop: &quot;{{ ds_linux_groups }}&quot;
    
- name: create linuxusers
  ldap_entry:
    dn: &quot;uid={{ item.uid }},{{ ds_users_dn }}&quot;
    state: present
    objectClass:
      - ldapPublicKey
      - posixAccount
      - shadowAccount
      - inetOrgPerson
    attributes:
      cn: &quot;{{ item.cn }}&quot;
      sn: &quot;{{ item.sn }}&quot;
      uid: &quot;{{ item.uid }}&quot;
      userPassword: &quot;{{ item.userPassword }}&quot;
      uidNumber: &quot;{{ item.uidNumber }}&quot;
      gidNumber: &quot;{{ item.gidNumber }}&quot;
      homeDirectory: &quot;{{ item.homeDirectory }}&quot;
      loginShell: &quot;{{ item.loginShell }}&quot;
      gecos: &quot;{{ item.gecos }}&quot;
      sshPublicKey: &quot;{{ item.sshPublicKey }}&quot;
    params: &quot;{{ ds_ldap_authentication }}&quot;
  loop: &quot;{{ ds_linux_users }}&quot;
&lt;/pre&gt;

When the user and group is not present we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@db1 /]# ls -l home/
total 0
drwx------. 1 ansible ansible  146 Mar 15 08:41 ansible
drwx------. 1 oracle  oinstall  92 Mar 16 06:34 oracle

[root@db1 /]# id testuser
id: testuser: no such user
&lt;/pre&gt;

When we add the user and group to the Directory Server we have the following

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# login using ssh key
[mobaxterm] ➤ ssh -i id_rsa testuser@db1.ms.com
...
Creating directory &#39;/home/testuser&#39;.
...

[testuser@db1 ~]$ ls -altr
total 16
-rw-------  1 testuser operators 172 Apr  7 07:52 .kshrc
-rw-------  1 testuser operators 231 Apr  7 07:52 .bashrc
-rw-------  1 testuser operators 193 Apr  7 07:52 .bash_profile
-rw-------  1 testuser operators  18 Apr  7 07:52 .bash_logout
drwxr-xr-x. 1 root     root       42 Apr  7 07:52 ..
drwx------  1 testuser operators  76 Apr  7 07:52 .

[testuser@db1 ~]$ id testuser
uid=10000(testuser) gid=5000(operators) groups=5000(operators)

[testuser@db1 ~]$ yum install dos2unix
Loaded plugins: ulninfo
You need to be root to perform this command.

[testuser@db1 ~]$ sudo yum install dos2unix
[sudo] password for testuser:
Loaded plugins: ulninfo
Package dos2unix-6.0.3-7.el7.x86_64 already installed and latest version
Nothing to do
&lt;/pre&gt;

An alternative to nss-pam-ldap (nscd) is &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#SSSD&quot; target=&quot;_blank&quot;&gt;sssd&lt;/a&gt;. As sssd is not designed to be used with nscd (more information can be found &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#usingnscd-sssd&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;), we will configure sssd on the test host. To configure sssd the following steps can be used:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
sssd_packages:
  - sssd
  - sssd-tools

sssd_conf_path: &quot;/etc/sssd/sssd.conf&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Install the sssd packages.&lt;/li&gt;
    &lt;li&gt;Configure sssd to use the Directory Server, more information can be found &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#Configuring_Domains-Configuring_a_Native_LDAP_Domain&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;, for example,
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# Config documentation links
#
# https://linux.die.net/man/5/sssd.conf
# https://linux.die.net/man/5/sssd-ldap
# https://linux.die.net/man/8/sss_cache
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#SSSD
#
#
[sssd]
config_file_version = 2
services = nss, pam
domains = test151

[nss]
filter_groups = root
filter_users = root
entry_cache_nowait_percentage = 75

[pam]
offline_credentials_expiration = 2
offline_failed_login_attempts = 3
offline_failed_login_delay = 5

[domain/test151]
ldap_uri = {{ ds_ldaps_authentication.server_uri }}
ldap_search_base = {{ ds_suffix }}
ldap_schema = rfc2307

id_provider = ldap
ldap_id_use_start_tls = true
enumerate = false
cache_credentials = true
entry_cache_timeout = 300

auth_provider = ldap
#ldap_tls_cacertdir = {{ ds_nss_database_path }}
ldap_tls_cacert = {{ ds_nss_database_ca_certificate_path }}
ldap_tls_reqcert = allow
ldap_default_bind_dn = {{ ds_ldaps_authentication.bind_dn }}
ldap_default_authtok_type = password
ldap_default_authtok = {{ ds_ldaps_authentication.bind_pw }}
ldap_group_search_base = {{ ds_groups_dn }}
ldap_user_search_base = {{ ds_users_dn }}

chpass_provider = ldap

access_provider = ldap
ldap_access_filter = (&amp;(objectclass=posixAccount)(gidNumber=5000))
&lt;/pre&gt;
    above also the system services nss and pam are configured (more information can be found &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#Configuring_Services&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;).
    &lt;/li&gt;
    &lt;li&gt;Configure user information service in &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt; and authentication functions in &lt;code&gt;/etc/pam.d/system-auth&lt;/code&gt; and &lt;code&gt;/etc/pam.d/password-auth&lt;/code&gt; by using the authentication configuration tool &lt;code&gt;&lt;a href=&quot;https://linux.die.net/man/8/authconfig&quot; target=&quot;_blank&quot;&gt;authconfig&lt;/a&gt;&lt;/code&gt;. For more information see the &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index&quot; target=&quot;_blank&quot;&gt;System-Level Authentication Guide&lt;/a&gt;.&lt;/li&gt;
    &lt;li&gt;Enable &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html-single/system-level_authentication_guide/index#authconfig-homedirs&quot; target=&quot;_blank&quot;&gt;custom home directories&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
- name: install sssd
  yum:
    name: &quot;{{ sssd_packages }}&quot;
    state: present
    
- name: enable and start sssd service
  systemd:
    name: sssd
    state: started
    enabled: yes
  register: sssd_started

- name: configure sssd
  template:
    src: roles/demo_sssd/templates/sssd_conf.j2
    dest: &quot;{{ sssd_conf_path }}&quot;
    owner: root
    group: root
    mode: 0600
  register: sssd_configured

- name: restart sssd service
  systemd:
    name: sssd
    state: restarted
  when: sssd_configured is changed

- name: check if authconfig sssd is enabled
  lineinfile:
    path: /etc/pam.d/system-auth
    line: &quot;auth        [default=1 success=ok] pam_localuser.so&quot;
    state: present
  check_mode: yes
  register: authconfig_sssd_enabled

- name: debug authconfig
  debug:
    var=authconfig_sssd_enabled
  when: authconfig_sssd_enabled is changed

- name: execute authconfig enable sssd
  command: /bin/sh -c &quot;/sbin/authconfig --update --enablesssd --enablesssdauth&quot;
  when: authconfig_sssd_enabled is changed

- name: check if authconfig mkhomedir is enabled
  lineinfile:
    path: /etc/pam.d/system-auth-ac
    line: &quot;session     optional      pam_mkhomedir.so umask=0077&quot;
    state: present
  check_mode: yes
  register: authconfig_mkhomedir_enabled

- name: debug authconfig
  debug:
    var=authconfig_mkhomedir_enabled
  when: authconfig_mkhomedir_enabled is changed

- name: execute authconfig enable mkhomedir
  command: /bin/sh -c &quot;/sbin/authconfig --update --enablemkhomedir&quot;
  when: authconfig_mkhomedir_enabled is changed
&lt;/pre&gt;

With the configuration in place let us test the set-up

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
[root@test151 ~]# systemctl start sssd

[root@test151 ~]# ll /var/lib/sss/db

[root@test151 ~]# tail -f /var/log/dirsrv/slapd-test151/access

[root@test151 ~]# id testuser
uid=10000(testuser) gid=5000(operators) groups=5000(operators)

[root@test151 ~]# getent passwd testuser
testuser:*:10000:5000:FullName:/home/testuser:/bin/bash

[root@test151 ~]# sssctl user-checks testuser --action=auth
user: testuser
action: auth
service: system-auth

SSSD nss user lookup result:
 - user name: testuser
 - user id: 10000
 - group id: 5000
 - gecos: FullName
 - home directory: /home/testuser
 - shell: /bin/bash

SSSD InfoPipe user lookup result:
 - name: testuser
 - uidNumber: 10000
 - gidNumber: 5000
 - gecos: FullName
 - homeDirectory: /home/testuser
 - loginShell: /bin/bash

testing pam_authenticate

Password:
pam_authenticate for user [testuser]: Success

PAM Environment:
 - no env -
 
[root@test151 ~]# systemctl stop sssd

[root@test151 ~]# id testuser
uid=10000(testuser) gid=5000(operators) groups=5000(operators)

[root@test151 ~]# sss_cache -E

[root@test151 ~]# id testuser
id: testuser: no such user

[root@test151 ~]# systemctl start sssd

[root@test151 ~]# sssctl user-checks testuser
user: testuser
action: acct
service: system-auth

SSSD nss user lookup result:
 - user name: testuser
 - user id: 10000
 - group id: 5000
 - gecos: FullName
 - home directory: /home/testuser
 - shell: /bin/bash

SSSD InfoPipe user lookup result:
 - name: testuser
 - uidNumber: 10000
 - gidNumber: 5000
 - gecos: FullName
 - homeDirectory: /home/testuser
 - loginShell: /bin/bash

testing pam_acct_mgmt

pam_acct_mgmt: Success

PAM Environment:
 - no env -
 
[root@test151 ~]# sssctl domain-status test151
Online status: Online
Active servers:
LDAP: test151.ms.com
Discovered LDAP servers:
- test151.ms.com

[root@test151 ~]# sssctl user-show testuser
Name: testuser
Cache entry creation date: 04/19/19 09:33:31
Cache entry last update time: 04/19/19 09:46:22
Cache entry expiration time: Expired
Initgroups expiration time: Expired
Cached in InfoPipe: No
&lt;/pre&gt;

When things are not working troubleshooting tips are given &lt;a href=&quot;https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system-level_authentication_guide/trouble&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
&lt;strong&gt;Application &amp; Infrastructure Monitoring Authentication, Authorization and Role Mapping&lt;/strong&gt; - For the application and infrastructure monitoring we use &lt;a href=&quot;https://mathias-kettner.com/cms.html&quot; target=&quot;_blank&quot;&gt;Check_MK&lt;/a&gt;. To set-up Check_MK we can use the following steps:

&lt;ul&gt;
    &lt;li&gt;Used variables:
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
cmk_software_directory: &quot;{{ software_directory }}/check_mk&quot;
cmk_install_file: &quot;check-mk-enterprise-1.5.0p14-el7-38.x86_64.rpm&quot;

cmk_site_name: &quot;support&quot;
cmk_site_conf_path: &quot;/opt/omd/sites/{{ cmk_site_name }}/etc/omd/site.conf&quot;

cmk_listen_port: &quot;80&quot;
&lt;/pre&gt;
    &lt;/li&gt;
    &lt;li&gt;Add the optional packages yum repository, for more information see the documentation &lt;a href=&quot;https://mathias-kettner.com/cms_install_packages.html#Red%20Hat%20and%20CentOS&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;
&lt;blockquote cite=&quot;https://mathias-kettner.com/cms_install_packages.html#Red%20Hat%20and%20CentOS&quot;&gt;
In order to be able to use EPEL on RedHat, the package sources for optional RPMs (at least for RedHat 6) are required, if these haven&#39;t already been installed during the operating system installation. Without these sources the freeradius-utils, graphviz-gd, and php-mbstring package will be missing.
&lt;/blockquote&gt;
    &lt;/li&gt;
    &lt;li&gt;Copy the Check_MK install file (rpm).&lt;/li&gt;
    &lt;li&gt;Create a monitoring instance, by using &lt;code&gt;/usr/bin/omd create {{ cmk_site_name }}&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Start the created monitoring instance.&lt;/li&gt;
    &lt;li&gt;Optionally change the admin passwords by using the &lt;a href=&quot;https://docs.ansible.com/ansible/latest/modules/htpasswd_module.html&quot; target=&quot;_blank&quot;&gt;htpasswd&lt;/a&gt; module.&lt;/li&gt;
&lt;/ul&gt;

To combine the above in an Ansible playbook, we have the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
# https://yum.oracle.com/repo/OracleLinux/OL7/optional/latest/x86_64/index.html
- name: add oracle linux 7 (x86_64) optional latest
  yum_repository:
    name: epel_optional_packages
    description: Oracle Linux 7 (x86_64) Optional Latest
    baseurl: http://yum.oracle.com/repo/OracleLinux/OL7/optional/latest/x86_64
    gpgcheck: no
    enabled: yes

- name: create software deploy directory
  file:
    path: &quot;{{ cmk_software_directory }}&quot;
    state: directory

- name: copy latest {{ cmk_install_file }}
  copy:
    src: &quot;/home/ansible/{{ cmk_install_file }}&quot;
    dest: &quot;{{ cmk_software_directory }}&quot;
  register: check_mk_software_copied

- name: install check_mk
  yum:
    name: &quot;{{ cmk_software_directory }}/{{ cmk_install_file }}&quot;
    state: present
  when: check_mk_software_copied is success
  register: check_mk_installed

- name: create monitoring instance {{ cmk_site_name }}
  command: /bin/sh -c &quot;/usr/bin/omd create {{ cmk_site_name }}&quot;
    creates=&quot;{{ cmk_site_conf_path }}&quot;
  when: check_mk_installed is success
  register: check_mk_instance_created
  
- name: debug monitoring instance {{ cmk_site_name }}
  debug:
    var=check_mk_instance_created
  when: check_mk_instance_created is changed

- name: start monitoring instance {{ cmk_site_name }}
  systemd:
    name: omd
    state: started
    enabled: yes
  register: omd_started
  
- name: change admin passwords
  htpasswd: 
    path: &quot;/omd/sites/{{ cmk_site_name }}/etc/htpasswd&quot;
    crypt_scheme: apr_md5_crypt
    name: &quot;{{ item }}&quot;
    password: password
    owner: &quot;{{ cmk_site_name }}&quot;
    group: &quot;{{ cmk_site_name }}&quot;
    mode: 0660
  loop:
    - cmkadmin
    - automation
&lt;/pre&gt;

With Check_MK installed and configured, we add an &lt;a href=&quot;https://mathias-kettner.com/cms_ldap.html&quot; target=&quot;_blank&quot;&gt;LDAP connection&lt;/a&gt;:

&lt;ul&gt;
    &lt;li&gt;The tool for configuring Check_MK via GUI is called the &lt;a href=&quot;https://mathias-kettner.com/cms_wato.html&quot; target=&quot;_blank&quot;&gt;WATO (Web Administration Tool)&lt;/a&gt;. Access the &lt;a href=&quot;https://mathias-kettner.com/cms_user_interface.html&quot; target=&quot;_blank&quot;&gt;user interface&lt;/a&gt; (using the &lt;code&gt;cmkadmin&lt;/code&gt; user), and click WATO-Configuration, Users, LDAP Connections, and add the following:&lt;/li&gt;
&lt;pre class=&quot;prettyprint linenums&quot;&gt;
--- GENERAL PROPERTIES
- ID: test
- Description: test users
--- LDAP CONNECTION
- Directory Type: 389 Directory Server
    - Connect to
        - Manually specify list of LDAP servers
            - LDAP Server: db1.ms.com
- Bind Credentials
    - Bind DN: cn=Directory Manager
    - Bind Password: password
- TCP Port: 389
--- USERS
- User Base DN: ou=people,dc=ms,dc=com
- Search Scope: Search all entries one level below the base DN
- Search Filter: (&amp;(objectclass=posixAccount)(gidNumber=5000))
- User-ID Attribute: uid
--- GROUPS
- Group Base DN: ou=groups,dc=ms,dc=com
- Search Scope: Search all entries one level below the base DN
- Search Filter: (objectclass=groupOfUniqueNames)
- Member Attribute: memberUid
--- ATTRIBUTE SYNC PLUGINS 
- Alias, LDAP attribute to sync: cn
- Authentication Expiration, LDAP attribute to be used as indicator: shadowExpire
- Contactgroup Membership
- Roles, Administrator, Group DN: cn=employees,ou=groups,dc=ms,dc=com, Search In: This connection
--- OTHER
- Sync interval: 1 days 0 hours 0 mins
&lt;/pre&gt;
&lt;/ul&gt;
With the configuration in place click &lt;code&gt;save and test&lt;/code&gt; and make the changes permanent by activatating the changes. Next, synchronize the users, and login with the &lt;code&gt;testuser&lt;/code&gt; account.
&lt;br/&gt;&lt;br/&gt;
I spent my entire life getting ready for this sort of future and it&#39;s going down easy - &lt;a href=&quot;https://dilbert.com/strip/2017-05-27&quot; target=&quot;_blank&quot;&gt;Wally&lt;/a&gt;.

&lt;h2&gt;References&lt;/h2&gt;
[1] &lt;a href=&quot;http://docs.ansible.com/&quot; target=&quot;_blank&quot;&gt;Ansible Documentation&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/1160492498204264715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/1160492498204264715'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/2019/04/a-little-bit-of-everything.html' title='A Little Bit of Everything'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-4896087204075281708.post-4131315989412535442</id><published>2017-12-10T12:07:00.001+01:00</published><updated>2018-07-05T10:49:08.568+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Database"/><category scheme="http://www.blogger.com/atom/ns#" term="JMS"/><category scheme="http://www.blogger.com/atom/ns#" term="Monitoring"/><category scheme="http://www.blogger.com/atom/ns#" term="Tuning"/><category scheme="http://www.blogger.com/atom/ns#" term="WebLogic"/><title type='text'>Message-Driven AQ</title><content type='html'>In this post we are going to combine &lt;a href=&quot;https://docs.oracle.com/javaee/6/tutorial/doc/gipko.html&quot; target=&quot;_blank&quot;&gt;message-driven beans&lt;/a&gt; ( running on &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/INTRO/intro.htm#INTRO123&quot; target=&quot;_blank&quot;&gt;WebLogic&lt;/a&gt; ) with &lt;a href=&quot;https://docs.oracle.com/database/121/ADQUE/aq_intro.htm#ADQUE0100&quot; target=&quot;_blank&quot;&gt;Oracle Database Advanced Queuing&lt;/a&gt;.
&lt;br/&gt;&lt;br/&gt;
To configure AQ is described in the document &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/INTRO/intro.htm#INTRO123&quot; target=&quot;_blank&quot;&gt;Interoperating with Oracle AQ JMS&lt;/a&gt;:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
connect / as sysdba;

-- create a new user
create user jmsuser identified by jmsuserpwd;
alter user jmsuser default tablespace users;
alter user jmsuser temporary tablespace temp;
grant dba to jmsuser;
grant aq_user_role to jmsuser;
grant execute on sys.dbms_aqadm to jmsuser;
grant execute on sys.dbms_aq to jmsuser;
grant execute on sys.dbms_aqin to jmsuser;
grant execute on sys.dbms_aqjms to jmsuser;

connect jmsuser / jmsuserpwd;

-- create AQ tables &lt;a href=&quot;https://docs.oracle.com/database/121/ARPLS/d_aqadm.htm#ARPLS106&quot; target=&quot;_blank&quot;&gt;dbms_aqadm.create_queue_table&lt;/a&gt;
execute dbms_aqadm.create_queue_table(queue_table=&gt;&#39;TestQueueTable&#39;, queue_payload_type=&gt;&#39;sys.aq$_jms_text_message&#39;, multiple_consumers=&gt;false);

-- create queue &lt;a href=&quot;https://docs.oracle.com/database/121/ARPLS/d_aqadm.htm#ARPLS109&quot; target=&quot;_blank&quot;&gt;dbms_aqadm.create_queue&lt;/a&gt;
execute dbms_aqadm.create_queue(queue_name=&gt;&#39;TestUserQueue&#39;, queue_table=&gt;&#39;TestQueueTable&#39;, max_retries=&gt;1, retry_delay=&gt;120);

-- start the queue &lt;a href=&quot;https://docs.oracle.com/database/121/ARPLS/d_aqadm.htm#ARPLS113&quot; target=&quot;_blank&quot;&gt;dbms_aqadm.start_queue&lt;/a&gt;
execute dbms_aqadm.start_queue(queue_name=&gt;&#39;TestUserQueue&#39;);
&lt;/pre&gt;

As we will be working with message-driven beans (acknowledgments and container managed transations), the data source and foreign server connection factory must be XA-enabled. The &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/jdbc/ConfigureDatabaseConnectivity.html&quot; target=&quot;_blank&quot;&gt;data source&lt;/a&gt; is configured as follows

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;jdbc-data-source ...&amp;gt;
    &amp;lt;name&amp;gt;aq_test_ds&amp;lt;/name&amp;gt;
    &amp;lt;datasource-type&amp;gt;GENERIC&amp;lt;/datasource-type&amp;gt;
    &amp;lt;jdbc-driver-params&amp;gt;
        &amp;lt;url&amp;gt;jdbc:oracle:thin:@//datanode.ms.com:1521/orcl12&amp;lt;/url&amp;gt;
        &amp;lt;driver-name&amp;gt;oracle.jdbc.xa.client.OracleXADataSource&amp;lt;/driver-name&amp;gt;
        &amp;lt;properties&amp;gt;
            &amp;lt;property&amp;gt;
                &amp;lt;name&amp;gt;user&amp;lt;/name&amp;gt;
                &amp;lt;value&amp;gt;jmsuser&amp;lt;/value&amp;gt;
            &amp;lt;/property&amp;gt;
        &amp;lt;/properties&amp;gt;
        &amp;lt;password-encrypted&amp;gt;{AES}LjD9zLInpLXURZ12kqQdcGIHqcjlIaTqRzu2U5WkJR0=&amp;lt;/password-encrypted&amp;gt;
    &amp;lt;/jdbc-driver-params&amp;gt;
    &amp;lt;jdbc-connection-pool-params&amp;gt;
        &amp;lt;test-table-name&amp;gt;SQL ISVALID&amp;lt;/test-table-name&amp;gt;
    &amp;lt;/jdbc-connection-pool-params&amp;gt;
    &amp;lt;jdbc-data-source-params&amp;gt;
        &amp;lt;jndi-name&amp;gt;jdbc/aq_test_ds&amp;lt;/jndi-name&amp;gt;
        &amp;lt;global-transactions-protocol&amp;gt;TwoPhaseCommit&amp;lt;/global-transactions-protocol&amp;gt;
    &amp;lt;/jdbc-data-source-params&amp;gt;
&amp;lt;/jdbc-data-source&amp;gt;
&lt;/pre&gt;

WebLogic AQ JMS supports both Global XA (JTA) transactions and local JMS transacted session transactions. Global transactions require use of XA based connection factories, while local transactions use non-XA based JMS connection factories:

&lt;ul&gt;
    &lt;li&gt;When selecting a non-XA JDBC driver, then we can only use WebLogic AQ JMS in local transactions.&lt;/li&gt;
    &lt;li&gt;When selecting an XA JDBC driver, then we can use WebLogic AQ JMS in both local and global transactions.&lt;/li&gt;
    &lt;li&gt;Note that this release does not support non-XA JDBC driver data sources with any of the global transaction options such as Logging Last Resource (LLR), One-Phase Commit (JTS), and Emulated Two-Phase Commit. If Supports Global Transactions is selected, then WebLogic Server logs a warning message.&lt;/li&gt;
    &lt;li&gt;Global transactions are supported only with an XA JDBC driver One-Phase commit optimization. If we use the same XA capable data source for both AQ JMS and JDBC operations, then the XA transactional behavior is equivalent to having two connections in a single data source that is treated as a single resource by the transaction manager. Therefore, if the AQ JMS and JDBC operations are called under the same JTA transaction, and no other resources are involved in the transaction, the transaction uses One-Phase Commit optimization instead of Two-Phase Commit; otherwise read-only optimization is used. More information can be found in the document &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/JMSPG/interop.htm#JMSPG555&quot; target=&quot;_blank&quot;&gt;Understanding Transactions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

The &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/pagehelp/Jmsjmsforeignjmsforeignjmsservertabletitle.html&quot; target=&quot;_blank&quot;&gt;foreign server&lt;/a&gt; is configured as follows

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;weblogic-jms ...&amp;gt;
    &amp;lt;foreign-server name=&quot;aq_test_foreign_server&quot;&amp;gt;
        &amp;lt;default-targeting-enabled&amp;gt;true&amp;lt;/default-targeting-enabled&amp;gt;
        &amp;lt;foreign-destination name=&quot;aq_test_foreign_destination&quot;&amp;gt;
            &amp;lt;local-jndi-name&amp;gt;jms/AQTestQueue&amp;lt;/local-jndi-name&amp;gt;
            &amp;lt;remote-jndi-name&amp;gt;Queues/TestUserQueue&amp;lt;/remote-jndi-name&amp;gt;
        &amp;lt;/foreign-destination&amp;gt;
        &amp;lt;foreign-connection-factory name=&quot;aq_test_foreign_connection_factory&quot;&amp;gt;
            &amp;lt;local-jndi-name&amp;gt;jms/AQTestConnectionFactory&amp;lt;/local-jndi-name&amp;gt;
            &amp;lt;remote-jndi-name&amp;gt;XAQueueConnectionFactory&amp;lt;/remote-jndi-name&amp;gt;
            &amp;lt;connection-health-checking&amp;gt;enabled&amp;lt;/connection-health-checking&amp;gt;
        &amp;lt;/foreign-connection-factory&amp;gt;
        &amp;lt;initial-context-factory&amp;gt;oracle.jms.AQjmsInitialContextFactory&amp;lt;/initial-context-factory&amp;gt;
        &amp;lt;jndi-property&amp;gt;
            &amp;lt;key&amp;gt;datasource&amp;lt;/key&amp;gt;
            &amp;lt;value&amp;gt;jdbc/aq_test_ds&amp;lt;/value&amp;gt;
        &amp;lt;/jndi-property&amp;gt;
    &amp;lt;/foreign-server&amp;gt;
&amp;lt;/weblogic-jms&amp;gt;
&lt;/pre&gt;

With the resources in-place, we continue with creating the message-driven bean. Message-driven beans, like other well-behaved MessageListeners, should not, in general, throw runtime exceptions. If a message-driven bean&#39;s onMessage method encounters a system-level exception or error that does not allow the method to successfully complete, the &lt;a href=&quot;http://download.oracle.com/otndocs/jcp/ejb-3_2-fr-spec/index.html&quot; target=&quot;_blank&quot;&gt;Enterprise JavaBeans Specification&lt;/a&gt; (JSR-000345 EJB 3.2 Core Specification, 9.2.2 System Exceptions) provides the following guidelines:

&lt;ul&gt;
    &lt;li&gt;If the enterprise bean method encounters a system-level exception or error that does not allow the method to successfully complete, the method should throw a suitable non-application exception that is compatible with the method’s throws clause. While the EJB specification does not prescribe the exact usage of the exception, it encourages the Bean Provider to follow these guidelines:
        &lt;ul&gt;
            &lt;li&gt;If the bean method encounters a system exception or error, it should simply propagate the error from the bean method to the container (i.e., the bean method does not have to catch the exception).&lt;/li&gt;
            &lt;li&gt;If the bean method performs an operation that results in a checked exception that the bean method cannot recover, the bean method should throw the &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/ejb/EJBException.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;javax.ejb.EJBException&lt;/code&gt;&lt;/a&gt; that wraps the original exception.&lt;/li&gt;
            &lt;li&gt;Any other unexpected error conditions should be reported using the &lt;code&gt;javax.ejb.EJBException&lt;/code&gt;.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

Note that the &lt;code&gt;javax.ejb.EJBException&lt;/code&gt; is a subclass of the &lt;code&gt;java.lang.RuntimeException&lt;/code&gt;, and therefore it does not have to be listed in the throws clauses of the business methods.
&lt;br/&gt;&lt;br/&gt;
Under container-managed transaction demarcation, upon receiving a runtime exception from a message-driven bean&#39;s &lt;code&gt;onMessage&lt;/code&gt; method, the container rolls back the container-started transaction and the message is redelivered (as was configured in the creation of de AQ queue - &lt;code&gt;max_retries=1&lt;/code&gt; and &lt;code&gt;retry_delay=120&lt;/code&gt;). This is because the message delivery itself is part of the container-started transaction. By default, the Application Server container closes the container&#39;s connection to the JMS provider when the first runtime exception is received from a message-driven bean instance&#39;s &lt;code&gt;onMessage&lt;/code&gt; method. This avoids potential message redelivery looping and protects server resources if the message-driven bean&#39;s &lt;code&gt;onMessage&lt;/code&gt; method continues misbehaving.
&lt;br/&gt;&lt;br/&gt;
Until a JMS message has been acknowledged, it is not considered to be successfully consumed. The successful consumption of a message ordinarily takes place in three stages:

&lt;ul&gt;
    &lt;li&gt;The client receives the message.&lt;/li&gt;
    &lt;li&gt;The client processes the message.&lt;/li&gt;
    &lt;li&gt;The message is acknowledged. Acknowledgment is initiated either by the JMS provider or by the client, depending on the session acknowledgment mode.&lt;/li&gt;
&lt;/ul&gt;

In transacted sessions, acknowledgment happens automatically when a transaction is committed. If a transaction is rolled back, all consumed messages are redelivered. In nontransacted sessions, when and how a message is acknowledged depend on the value specified as the second argument of the &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Connection.html#createSession-boolean-int-&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;createSession&lt;/code&gt;&lt;/a&gt; method. The three possible argument values are as follows:

&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Session.html#AUTO_ACKNOWLEDGE&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Session.AUTO_ACKNOWLEDGE&lt;/code&gt;&lt;/a&gt;: The session automatically acknowledges a client&#39;s receipt of a message either when the client has successfully returned from a call to receive or when the MessageListener it has called to process the message returns successfully. A synchronous receive in an &lt;code&gt;AUTO_ACKNOWLEDGE&lt;/code&gt; session is the one exception to the rule that message consumption is a three-stage process as described earlier. In this case, the receipt and acknowledgment take place in one step, followed by the processing of the message.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Session.html#CLIENT_ACKNOWLEDGE&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Session.CLIENT_ACKNOWLEDGE&lt;/code&gt;&lt;/a&gt;: A client acknowledges a message by calling the message&#39;s acknowledge method. In this mode, acknowledgment takes place on the session level: Acknowledging a consumed message automatically acknowledges the receipt of all messages that have been consumed by its session. For example, if a message consumer consumes ten messages and then acknowledges the fifth message delivered, all ten messages are acknowledged. In the Java EE platform, a &lt;code&gt;CLIENT_ACKNOWLEDGE&lt;/code&gt; session can be used only in an application client, not in a web component or enterprise bean.&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Session.html#DUPS_OK_ACKNOWLEDGE&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Session.DUPS_OK_ACKNOWLEDGE&lt;/code&gt;&lt;/a&gt;: This option instructs the session to lazily acknowledge the delivery of messages. This is likely to result in the delivery of some duplicate messages if the JMS provider fails, so it should be used only by consumers that can tolerate duplicate messages. (If the JMS provider redelivers a message, it must set the value of the &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Message.html#setJMSRedelivered-boolean-&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;JMSRedelivered&lt;/code&gt;&lt;/a&gt; message header to true.) This option can reduce session overhead by minimizing the work the session does to prevent duplicates.&lt;/li&gt;
&lt;/ul&gt;

If messages have been received from a queue but not acknowledged when a session terminates, the JMS provider retains them and redelivers them when a consumer next accesses the queue. The provider also retains unacknowledged messages for a terminated session that has a durable &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/TopicSubscriber.html&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;TopicSubscriber&lt;/code&gt;&lt;/a&gt;. Unacknowledged messages for a nondurable &lt;code&gt;TopicSubscriber&lt;/code&gt; are dropped when the session is closed.
&lt;br/&gt;&lt;br/&gt;
When using a queue or a durable subscription, we can use the &lt;a href=&quot;https://docs.oracle.com/javaee/7/api/javax/jms/Session.html#recover--&quot; target=&quot;_blank&quot;&gt;&lt;code&gt;Session.recover&lt;/code&gt;&lt;/a&gt; method to stop a nontransacted session and restart it with its first unacknowledged message. In effect, the session&#39;s series of delivered messages is reset to the point after its last acknowledged message. The messages it now delivers may be different from those that were originally delivered, if messages have expired or if higher-priority messages have arrived. For a nondurable &lt;code&gt;TopicSubscriber&lt;/code&gt;, the provider may drop unacknowledged messages when its session is recovered. More information can be found in the document &lt;a href=&quot;https://docs.oracle.com/javaee/6/tutorial/doc/bncdq.html&quot; target=&quot;_blank&quot;&gt;Java Message Service Concepts&lt;/a&gt;. An example message-driven bean is given in the document &lt;a href=&quot;https://docs.oracle.com/javaee/6/tutorial/doc/bnbpo.html&quot; target=&quot;_blank&quot;&gt;The Message-Driven Bean Class&lt;/a&gt;. Our message-driven bean class looks as follows

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
package model.logic;

import javax.ejb.EJBException;
import javax.jms.*;

public class AQTestMDB implements MessageListener {
    @Override
    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;

            String text = null;

            try {
                text = textMessage.getText();
            } catch (JMSException e) {
                e.printStackTrace();
            }

            if (text.contains(&quot;exception&quot;)) {
                throw new EJBException(&quot;text contains exception&quot;);
            } else {
                System.out.println(&quot;RECEIVED TEXT MESSAGE &quot; + text);
            }
        }
    }
}
&lt;/pre&gt;

The document &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/JMSAD/aq_jms.htm#JMSAD-GUID-009CF72A-88FA-4255-B750-8DA551E84AE3&quot; target=&quot;_blank&quot;&gt;Settings for Message Driven Beans to Interoperate with AQ JMS&lt;/a&gt; contains information on how the message-driven bean should be configured. In our case, we use the following:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
META-INF/ejb-jar.xml
&amp;lt;ejb-jar ...&amp;gt;
    &amp;lt;enterprise-beans&amp;gt;
        &amp;lt;message-driven&amp;gt;
            &amp;lt;ejb-name&amp;gt;AQTestMDB&amp;lt;/ejb-name&amp;gt;
            &amp;lt;mapped-name&amp;gt;jms/AQTestQueue&amp;lt;/mapped-name&amp;gt;
            &amp;lt;ejb-class&amp;gt;model.logic.AQTestMDB&amp;lt;/ejb-class&amp;gt;
            &amp;lt;transaction-type&amp;gt;Container&amp;lt;/transaction-type&amp;gt;
            &amp;lt;message-destination-type&amp;gt;javax.jms.Queue&amp;lt;/message-destination-type&amp;gt;
        &amp;lt;/message-driven&amp;gt;
    &amp;lt;/enterprise-beans&amp;gt;
&amp;lt;/ejb-jar&amp;gt;

META-INF/weblogic-ejb-jar.xml
&amp;lt;weblogic-ejb-jar ...&amp;gt;
    &amp;lt;weblogic-enterprise-bean&amp;gt;
        &amp;lt;ejb-name&amp;gt;AQTestMDB&amp;lt;/ejb-name&amp;gt;
        &amp;lt;message-driven-descriptor&amp;gt;
            &amp;lt;pool&amp;gt;
                &amp;lt;max-beans-in-free-pool&amp;gt;1&amp;lt;/max-beans-in-free-pool&amp;gt;
                &amp;lt;initial-beans-in-free-pool&amp;gt;1&amp;lt;/initial-beans-in-free-pool&amp;gt;
            &amp;lt;/pool&amp;gt;
            &amp;lt;destination-jndi-name&amp;gt;jms/AQTestQueue&amp;lt;/destination-jndi-name&amp;gt;
            &amp;lt;connection-factory-jndi-name&amp;gt;jms/AQTestConnectionFactory&amp;lt;/connection-factory-jndi-name&amp;gt;
        &amp;lt;/message-driven-descriptor&amp;gt;
    &amp;lt;/weblogic-enterprise-bean&amp;gt;
&amp;lt;/weblogic-ejb-jar&amp;gt;
&lt;/pre&gt;

&lt;a href=&quot;https://docs.oracle.com/cd/E19798-01/821-1841/6nmq2cp4f/index.html&quot; target=&quot;_blank&quot;&gt;Package&lt;/a&gt; the application and &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/applications/ApplicationOverview.html&quot; target=&quot;_blank&quot;&gt;deploy&lt;/a&gt; it to WebLogic.
&lt;br/&gt;&lt;br/&gt;
To test the set-up we need to put a few messages on the queue. The document &lt;a href=&quot;https://docs.oracle.com/database/121/ADQUE/jm_exmpl.htm#ADQUE3189&quot; target=&quot;_blank&quot;&gt;More Oracle Database Advanced Queuing JMS Examples&lt;/a&gt; shows how do accomplish this, i.e.,

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
create or replace procedure enqueue_text( text in varchar2 ) as
    agent               sys.aq$_agent := sys.aq$_agent(&#39; &#39;, null, 0);
    message             sys.aq$_jms_text_message;
    enqueue_options     dbms_aq.enqueue_options_t;
    message_properties  dbms_aq.message_properties_t;
    msgid               raw(16);
begin
    message := sys.aq$_jms_text_message.construct;
    message.set_text(text);
    
    dbms_aq.enqueue(queue_name =&gt; &#39;testuserqueue&#39;, enqueue_options =&gt; enqueue_options, message_properties =&gt; message_properties, payload =&gt; message, msgid =&gt; msgid);
end;
&lt;/pre&gt;

which can be called as follows

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
declare
    text varchar2(200);
begin
    text := &#39;A little bit of good text&#39;;
    enqueue_text( text =&gt; text );
end;
&lt;/pre&gt;

In the case the variable &lt;code&gt;text&lt;/code&gt; contains the word &lt;code&gt;exception&lt;/code&gt; an exception will be raised

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;Nov 29, 2017 2:00:58,485 PM CET&amp;gt; &amp;lt;Warning&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010065&amp;gt; &amp;lt;MessageDrivenBean threw an Exception in onMessage(). The exception is:
 javax.ejb.EJBException: text contains exception.
javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:30)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        Truncated. see log file for complete stacktrace
&amp;gt;
&amp;lt;Nov 29, 2017 2:00:58,507 PM CET&amp;gt; &amp;lt;Error&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010080&amp;gt; &amp;lt;An error occurred while attempting to process a message inside a Message-Driven Bean: javax.ejb.EJBException: text contains exception
Exception is : javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:30)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:670)
        at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:352)
        at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:337)
        at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:57)
        at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
        at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:644)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:415)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:355)
&amp;gt;
&amp;lt;Nov 29, 2017 2:02:59,588 PM CET&amp;gt; &amp;lt;Error&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010080&amp;gt; &amp;lt;An error occurred while attempting to process a message inside a Message-Driven Bean: javax.ejb.EJBException: text contains exception
Exception is : javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:30)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:670)
        at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:352)
        at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:337)
        at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:57)
        at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
        at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:644)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:415)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:355)
&amp;gt;
RECEIVED TEXT MESSAGE A little bit of good text
&lt;/pre&gt;

When the message does not contain the word &lt;code&gt;exception&lt;/code&gt; the text &lt;code&gt;RECEIVED TEXT MESSAGE ...&lt;/code&gt; will be printed in the WebLogic .out-file.
&lt;br/&gt;&lt;br/&gt;
As can be seen from the logging, a &#39;wrong&#39; message is redelivered after two minutes (after which it again fails and is redirected to an error-queue).

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdnIov8LkFQBKoC3ydK17KoYp22EKkDxaaMXuA0OO8vbTY1TxpAuw5jraRMuhmQGDcu1SAf6U06kDZ1QiU-POtuNhfN1pArDzZqKvs1FrAMXxl6nUjfHEJyBWdNvF0aycM31jYRhJRWC_L/s640/testqueuetable_message_blijft_op_de_queue.png&quot; alt=&quot;testqueuetable_message_blijft_op_de_queue&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

In WebLogic we have the following statistics for the message-driven bean


&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGq1hjseqTc1cvdRFFPRy_pbvqdBKrRhyphenhyphenGCKs2SHCQWGZ3uwmKrhflRAQLH_-eex-QQspgsANS1OwBAlwQ2MMKNUIgIkcuS9SD3TUZEHs9QsbCo9gvObkHHa6juvZDH8JDjS50DE3u7lV6/s640/mdb_monitoring_weblogic.png&quot; alt=&quot;mdb_monitoring_weblogic&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

Two commited transactions (&lt;code&gt;text&lt;/code&gt; does not contain the word &lt;code&gt;exception&lt;/code&gt;), two transactions rolled back (one message containing the word &lt;code&gt;exception&lt;/code&gt; and a redilvery of that message).
&lt;br/&gt;&lt;br/&gt;
To make things a bit more interesting; instead of writing the message to a log file, we produce the message to a WebLogic Queue. To accomplish this we change the code to

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
package model.logic;

import javax.ejb.EJBException;
import javax.jms.*;

public class AQTestMDB implements MessageListener {

    private ConnectionFactory testConnectionFactory;
    private Queue testQueue;

    @Override
    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;

            String text = null;

            try {
                text = textMessage.getText();
            } catch (JMSException e) {
                throw new EJBException(e);
            }

            if (text.contains(&quot;exception&quot;)) {
                throw new EJBException(&quot;text contains exception&quot;);
            } else {
                try {
                    sendMessage(text);
                    System.out.println(&quot;TEXT MESSAGE &quot; + text + &quot; SEND.&quot;);
                } catch (JMSException e) {
                    throw new EJBException(e);
                }
            }
        }
    }

    private void sendMessage(String text) throws JMSException {
        Connection connection = null;
        Session session = null;
        try {
            connection = testConnectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer = session.createProducer(testQueue);

            TextMessage message = session.createTextMessage();
            message.setText(text);
            messageProducer.send(message);
        } finally {
            if (session != null) {
                session.close();
            }
            if (connection != null) {
                connection.close();
            }
        }
    }
}
&lt;/pre&gt;

with the following configuration

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
ejb-jar.xml
&amp;lt;ejb-jar ...&amp;gt;
    &amp;lt;enterprise-beans&amp;gt;
        &amp;lt;message-driven&amp;gt;
            &amp;lt;ejb-name&amp;gt;AQTestMDB&amp;lt;/ejb-name&amp;gt;
            &amp;lt;mapped-name&amp;gt;jms/AQTestQueue&amp;lt;/mapped-name&amp;gt;
            &amp;lt;ejb-class&amp;gt;model.logic.AQTestMDB&amp;lt;/ejb-class&amp;gt;
            &amp;lt;messaging-type&amp;gt;javax.jms.MessageListener&amp;lt;/messaging-type&amp;gt;
            &amp;lt;transaction-type&amp;gt;Container&amp;lt;/transaction-type&amp;gt;
            &amp;lt;message-destination-type&amp;gt;javax.jms.Queue&amp;lt;/message-destination-type&amp;gt;

            &amp;lt;resource-ref&amp;gt;
                &amp;lt;res-ref-name&amp;gt;jms/TestConnectionFactory&amp;lt;/res-ref-name&amp;gt;
                &amp;lt;res-type&amp;gt;javax.jms.ConnectionFactory&amp;lt;/res-type&amp;gt;
                &amp;lt;res-auth&amp;gt;Container&amp;lt;/res-auth&amp;gt;
                &amp;lt;res-sharing-scope&amp;gt;Shareable&amp;lt;/res-sharing-scope&amp;gt;
                &amp;lt;injection-target&amp;gt;
                    &amp;lt;injection-target-class&amp;gt;model.logic.AQTestMDB&amp;lt;/injection-target-class&amp;gt;
                    &amp;lt;injection-target-name&amp;gt;testConnectionFactory&amp;lt;/injection-target-name&amp;gt;
                &amp;lt;/injection-target&amp;gt;
            &amp;lt;/resource-ref&amp;gt;

            &amp;lt;resource-ref&amp;gt;
                &amp;lt;res-ref-name&amp;gt;jms/TestQueue&amp;lt;/res-ref-name&amp;gt;
                &amp;lt;res-type&amp;gt;javax.jms.Queue&amp;lt;/res-type&amp;gt;
                &amp;lt;res-auth&amp;gt;Container&amp;lt;/res-auth&amp;gt;
                &amp;lt;res-sharing-scope&amp;gt;Shareable&amp;lt;/res-sharing-scope&amp;gt;
                &amp;lt;injection-target&amp;gt;
                    &amp;lt;injection-target-class&amp;gt;model.logic.AQTestMDB&amp;lt;/injection-target-class&amp;gt;
                    &amp;lt;injection-target-name&amp;gt;testQueue&amp;lt;/injection-target-name&amp;gt;
                &amp;lt;/injection-target&amp;gt;
            &amp;lt;/resource-ref&amp;gt;

        &amp;lt;/message-driven&amp;gt;
    &amp;lt;/enterprise-beans&amp;gt;
&amp;lt;/ejb-jar&amp;gt;

 
weblogic-ejb-jar.xml (not changed)
&amp;lt;weblogic-ejb-jar ...&amp;gt;
    &amp;lt;weblogic-enterprise-bean&amp;gt;
        &amp;lt;ejb-name&amp;gt;AQTestMDB&amp;lt;/ejb-name&amp;gt;
        &amp;lt;message-driven-descriptor&amp;gt;
            &amp;lt;pool&amp;gt;
                &amp;lt;max-beans-in-free-pool&amp;gt;1&amp;lt;/max-beans-in-free-pool&amp;gt;
                &amp;lt;initial-beans-in-free-pool&amp;gt;1&amp;lt;/initial-beans-in-free-pool&amp;gt;
            &amp;lt;/pool&amp;gt;
            &amp;lt;destination-jndi-name&amp;gt;jms/AQTestQueue&amp;lt;/destination-jndi-name&amp;gt;
            &amp;lt;connection-factory-jndi-name&amp;gt;jms/AQTestConnectionFactory&amp;lt;/connection-factory-jndi-name&amp;gt;
        &amp;lt;/message-driven-descriptor&amp;gt;
    &amp;lt;/weblogic-enterprise-bean&amp;gt;
&amp;lt;/weblogic-ejb-jar&amp;gt;
&lt;/pre&gt;

In WebLogic, we have added the following &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/taskhelp/jms_modules/connection_factories/ConfigureConnectionFactories.html&quot; target=&quot;_blank&quot;&gt;connection factory&lt;/a&gt; and &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/WLACH/pagehelp/JMSjmsdistributeddestinationsqueuetabletitle.html&quot; target=&quot;_blank&quot;&gt;distributed queue&lt;/a&gt;

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
&amp;lt;weblogic-jms ...&amp;gt;
    &amp;lt;connection-factory name=&quot;TestConnectionFactory&quot;&amp;gt;
        &amp;lt;default-targeting-enabled&amp;gt;true&amp;lt;/default-targeting-enabled&amp;gt;
        &amp;lt;jndi-name&amp;gt;jms/TestConnectionFactory&amp;lt;/jndi-name&amp;gt;
        &amp;lt;client-params&amp;gt;
            &amp;lt;client-id-policy&amp;gt;Restricted&amp;lt;/client-id-policy&amp;gt;
            &amp;lt;subscription-sharing-policy&amp;gt;Exclusive&amp;lt;/subscription-sharing-policy&amp;gt;
            &amp;lt;messages-maximum&amp;gt;10&amp;lt;/messages-maximum&amp;gt;
        &amp;lt;/client-params&amp;gt;
        &amp;lt;transaction-params&amp;gt;
            &amp;lt;xa-connection-factory-enabled&amp;gt;true&amp;lt;/xa-connection-factory-enabled&amp;gt;
        &amp;lt;/transaction-params&amp;gt;
        &amp;lt;security-params&amp;gt;
            &amp;lt;attach-jmsx-user-id&amp;gt;false&amp;lt;/attach-jmsx-user-id&amp;gt;
        &amp;lt;/security-params&amp;gt;
    &amp;lt;/connection-factory&amp;gt;
    &amp;lt;foreign-server name=&quot;aq_test_foreign_server&quot;&amp;gt;
        ...
    &amp;lt;/foreign-server&amp;gt;
    &amp;lt;uniform-distributed-queue name=&quot;TestQueue&quot;&amp;gt;
        &amp;lt;sub-deployment-name&amp;gt;aq_test_sub_deployment&amp;lt;/sub-deployment-name&amp;gt;
        &amp;lt;default-targeting-enabled&amp;gt;false&amp;lt;/default-targeting-enabled&amp;gt;
        &amp;lt;jndi-name&amp;gt;jms/TestQueue&amp;lt;/jndi-name&amp;gt;
        &amp;lt;load-balancing-policy&amp;gt;Round-Robin&amp;lt;/load-balancing-policy&amp;gt;
        &amp;lt;forward-delay&amp;gt;-1&amp;lt;/forward-delay&amp;gt;
        &amp;lt;reset-delivery-count-on-forward&amp;gt;true&amp;lt;/reset-delivery-count-on-forward&amp;gt;
    &amp;lt;/uniform-distributed-queue&amp;gt;
&amp;lt;/weblogic-jms&amp;gt;
&lt;/pre&gt;

To test the set-up, we put some messages on the AQ queue

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
declare
    text varchar2(200);
begin
    text := &#39;something exceptional&#39;;
    enqueue_text( text =&gt; text );
end;
&lt;/pre&gt;

After sending a pair of messages the WebLogic logging shows the following

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
TEXT MESSAGE something useful SEND.
&amp;lt;Dec 5, 2017 10:41:34,609 AM CET&amp;gt; &amp;lt;Warning&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010065&amp;gt; &amp;lt;MessageDrivenBean threw an Exception in onMessage(). The exception is:
 javax.ejb.EJBException: text contains exception.
javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:25)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        Truncated. see log file for complete stacktrace
&amp;gt;
&amp;lt;Dec 5, 2017 10:41:34,624 AM CET&amp;gt; &amp;lt;Error&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010080&amp;gt; &amp;lt;An error occurred while attempting to process a message inside a Message-Driven Bean: javax.ejb.EJBException: text contains exception
Exception is : javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:25)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:670)
        at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:352)
        at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:337)
        at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:57)
        at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
        at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:644)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:415)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:355)
&amp;gt;
&amp;lt;Dec 5, 2017 10:43:35,661 AM CET&amp;gt; &amp;lt;Error&amp;gt; &amp;lt;EJB&amp;gt; &amp;lt;BEA-010080&amp;gt; &amp;lt;An error occurred while attempting to process a message inside a Message-Driven Bean: javax.ejb.EJBException: text contains exception
Exception is : javax.ejb.EJBException: text contains exception
        at model.logic.AQTestMDB.onMessage(AQTestMDB.java:25)
        at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:438)
        at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:361)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.processOneMessage(TokenBasedJMSMessagePoller.java:355)
        at weblogic.ejb.container.internal.TokenBasedJMSMessagePoller.run(TokenBasedJMSMessagePoller.java:125)
        at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:670)
        at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:352)
        at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:337)
        at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:57)
        at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
        at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:644)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:415)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:355)
&amp;gt;
&lt;/pre&gt;

Again a &#39;wrong&#39; message is retried, and after a retry put in an AQ error queue

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTFQg7hVheiByOECzF3mH9va7ND4n_QJeZWd_cJOj8rLYpgYfFCbyRyLdcJuIzcnpCSuDZaYYIiBvQqcMdQcFjtQKDs0F4_CvvuygWLMROU5YfedJ5xcYN4pRJTPr2k5GDfHifeYtrtHXY/s640/sql_monitor.png&quot; alt=&quot;sql_monitor&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

while a &#39;good&#39; message is put in the WebLogic queue

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkKBGvS-As8aR2SGNG_F-PSNdxBwplHtWvc-iMt-bCPL8aFkRY22-TKgUif4ZshKYsN6wIrzB8S4ktwD-0SjEQCdCEboEt5jrfqFLgXK-TI_Fc8DfAcAE_8C3ecsPLCTblI2_yP2xhEblk/s640/weblogic_messages.png&quot; alt=&quot;weblogic_messages&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

&lt;div style=&quot;width:100%; text-align:center&quot;&gt;
&lt;img src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOV-_mse4szAuppFI7SwbE1pCJvF2Yz_BaCiMuwP2S2EwpBtlrJ-D91mROFFq2CJ0v1Cvgpu8XqEFfjeHtuDvDySljIwH660_Y5TArys2CVFwM16ZnrnxXmgb102XIbcPQYZ9vK-LSAOEM/s640/weblogic_message_detail.png&quot; alt=&quot;weblogic_message_detail&quot; align=&quot;middle&quot;/&gt;
&lt;/div&gt;

for further processing.
&lt;br/&gt;&lt;br/&gt;
Information to tune message-driven beans can be found in the document &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/PERFM/mdbtuning.htm#PERFM271&quot; target=&quot;_blank&quot;&gt;Tuning Message-Driven Beans&lt;/a&gt;. Of special interrest is the &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/PERFM/mdbtuning.htm#PERFM279&quot; target=&quot;_blank&quot;&gt;message-driven bean thread management&lt;/a&gt;

&lt;ul&gt;
    &lt;li&gt;Number of concurrent message-driven beans: min(max-beans-in-free-pool,16)&lt;/li&gt;
    &lt;li&gt;Transactional WebLogic message-driven beans use a synchronous polling mechanism to retrieve messages from JMS destinations if they are either:
        &lt;ul&gt;
            &lt;li&gt;listening to non-WebLogic queues; or&lt;/li&gt;
            &lt;li&gt;listening to a WebLogic queue and transaction batching is enabled.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

When using &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/PERFM/mdbtuning.htm#PERFM279&quot; target=&quot;_blank&quot;&gt;message-driven beans with foreign destinations&lt;/a&gt; we have the following considerations

&lt;ul&gt;
    &lt;li&gt;The concurrency of message-driven beans that consume from destinations hosted by foreign providers (non-WebLogic JMS destinations) is determined using the same algorithm that is used for WebLogic JMS destinations.&lt;/li&gt;
    &lt;li&gt;Message-Driven beans listening on queues - Instead of a dedicated thread, each deployed message-driven bean uses a token-based, synchronous polling mechanism that always uses at least one thread from the dispatch-policy.&lt;/li&gt;
&lt;/ul&gt;

Here &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/PERFM/mdbtuning.htm#PERFM283&quot; target=&quot;_blank&quot;&gt;token-based message polling&lt;/a&gt; works as follows

&lt;ul&gt;
    &lt;li&gt;As of WebLogic 10.3, the polling mechanism changed to a token-based approach to provide better control of the concurrent poller thread count under changing message loads. In previous releases, the thread count ramp-up could be too gradual in certain use cases. Additionally, child pollers, once awoken, could not be ramped down and returned back to the pool for certain foreign JMS providers.&lt;/li&gt;
    &lt;li&gt;When a thread is returned to the thread pool with token-based polling, the thread&#39;s internal JMS consumer is closed rather than cached. This assures that messages will not be implicitly pre-fetched by certain foreign JMS Providers while there is no polling thread servicing the consumer.&lt;/li&gt;
    &lt;li&gt;In addition, each message-driven bean maintains a single token that provides permission for a given poller thread to create another thread.
        &lt;ul&gt;
            &lt;li&gt;On receipt of a message - A poller thread that already has the token or that is able to acquire the token because the token is not owned, wakes up an additional poller thread and gives the token to the new poller if the maximum concurrency has not yet been reached. If maximum concurrency has been reached, the poller thread simply releases the token (leaving it available to any other poller).&lt;/li&gt;
            &lt;li&gt;On finding an empty queue/topic - A poller tries to acquire the token and if successful will try to poll the queue periodically. If it fails to acquire the token, it returns itself back to the pool. This ensures that with an empty queue or topic, there is still at least one poller checking for messages.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;WebLogic 12.2.1.2.0 introduces two properties of type activation-configproperty: &lt;code&gt;mdbDestinationPollIntervalMillis&lt;/code&gt; and &lt;code&gt;minimizeAQSessions&lt;/code&gt;.
        &lt;ul&gt;
            &lt;li&gt;The &lt;code&gt;mdbDestinationPollInternvalMillis&lt;/code&gt; property controls the message polling interval that is used by the synchronous polling mechanism for message-driven bean.&lt;/li&gt;
            &lt;li&gt;When the JMS provider is AQJMS, the &lt;code&gt;minimizeAQSessions&lt;/code&gt; property reduces the use of database resources by minimizing the number of AQ JMS sessions.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

Additional considerations are described in the Oracle Support Doc ID 1502885.1

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
// Reduce sleep time between pools to 10ms and max of 15ms.
// the default =15secs causing messages to be queued up.
-Doracle.jms.minSleepTime=100
-Doracle.jms.maxSleepTime=200

// The parameter weblogic.ejb.container.MDBMessageWaitTime is measured in seconds.
// With Patch 13415672 this can be specified as a float as well.
// The minimum adviced value for this parameter is 250 ms (i.e. -Dweblogic.ejb.container.MDBMessageWaitTime=.25)
-Dweblogic.ejb.container.MDBMessageWaitTime=1

// Prevents simultaneous DB connection attempts by the MDBs (during startup)
-Dweblogic.ejb.container.MDBDestinationPollIntervalMillis=5000

// Free Resources
-Dweblogic.mdb.message.MinimizeAQSessions=true
&lt;/pre&gt;

Oracle Support Doc ID 1319580.1 and Doc ID 388291.1 provide some additional background information considering the above settings:
&lt;ul&gt;
    &lt;li&gt;Non-transactional Message-Driven Beans use a foreign vendor&#39;s thread, not a WebLogic Server thread. In this situation, a &lt;code&gt;dispatch-policy&lt;/code&gt; is ignored except for determining concurrency. In this case, a polling approach is used as well, i.e., it uses an exponential back off strategy to calculate the sleep time between two consecutive polls when the destination is empty. The sleep time can be controlled by the following system properties &lt;code&gt;-Doracle.jms.minSleepTime&lt;/code&gt; and &lt;code&gt;-Doracle.jms.maxSleepTime&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Transactional Message-Driven Beans run in WebLogic Server threads, as follows:
        &lt;ul&gt;
            &lt;li&gt;Message-Driven Beans listening on topics: Each deployed Message-Driven Bean uses a dedicated daemon polling thread that is created in Non-Pooled Threads thread group.&lt;/li&gt;
            &lt;li&gt;Message-Driven Beans listening on queues: Instead of a dedicated thread, each deployed Message-Driven Bean uses a token-based, synchronous polling mechanism that always uses at least one thread from a &lt;code&gt;dispatch-policy&lt;/code&gt;. In this case the &lt;code&gt;JMSMessagePoller&lt;/code&gt; is used, which has a polling latency that defaults to 3 seconds when the queue is empty. This default latency can be controlled with the &lt;code&gt;-Dweblogic.ejb.container.MDBMessageWaitTime&lt;/code&gt; system property which is measured in seconds.&lt;/li&gt;
        &lt;/ul&gt;
&lt;/ul&gt;

For transacted Message-Driven Beans the following setings are advised: &lt;code&gt;-Doracle.jms.minSleepTime=100 -Doracle.jms.maxSleepTime=200 -Dweblogic.ejb.container.MDBMessageWaitTime=1&lt;/code&gt;.

&lt;h2&gt;References&lt;/h2&gt;
[1] &lt;a href=&quot;https://docs.oracle.com/database/121/index.htm&quot; target=&quot;_blank&quot;&gt;Oracle Database Online Documentation&lt;/a&gt;.&lt;/br&gt;
[2] &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/index.html&quot; target=&quot;_blank&quot;&gt;Oracle WebLogic Server&lt;/a&gt;.&lt;/br&gt;
[3] &lt;a href=&quot;https://docs.oracle.com/javaee/7/tutorial/&quot; target=&quot;_blank&quot;&gt;Java Platform, Enterprise Edition: The Java EE Tutorial&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/4131315989412535442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/4131315989412535442'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/2017/12/message-driven-aq.html' title='Message-Driven AQ'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdnIov8LkFQBKoC3ydK17KoYp22EKkDxaaMXuA0OO8vbTY1TxpAuw5jraRMuhmQGDcu1SAf6U06kDZ1QiU-POtuNhfN1pArDzZqKvs1FrAMXxl6nUjfHEJyBWdNvF0aycM31jYRhJRWC_L/s72-c/testqueuetable_message_blijft_op_de_queue.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-4896087204075281708.post-5562232150285579251</id><published>2017-09-19T14:20:00.000+02:00</published><updated>2017-09-26T09:27:50.151+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cluster"/><category scheme="http://www.blogger.com/atom/ns#" term="Database"/><category scheme="http://www.blogger.com/atom/ns#" term="Monitoring"/><category scheme="http://www.blogger.com/atom/ns#" term="Operating System"/><category scheme="http://www.blogger.com/atom/ns#" term="Tuning"/><category scheme="http://www.blogger.com/atom/ns#" term="WebLogic"/><title type='text'>Fun with Virtualization</title><content type='html'>The successful deployment of Software on virtualization platforms is not significantly different from deploying Software on physical servers. We can fully leverage our current skill set while also delivering the benefits associated with virtualization. There are all kinds of &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/performance/Perf_Best_Practices_vSphere65.pdf&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt; out there that can help us leveraging best pratices in virtualization. Below, we present a summary (or checklist) of these best practices.

&lt;h2&gt;CPU&lt;/h2&gt;

&lt;h3&gt;Total number of VCPUs assigned to all VMs is equal to or less than the total number of physical cores&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;Do not over-allocate vCPUs try to match the exact workload. If monitoring of the actual workload shows that the Software is not benefiting from the increased vCPUs, the excess vCPUs impose scheduling constraints and can degrade overall performance of the virtual machine.&lt;/li&gt;
    &lt;li&gt;For Business Critical Applications ( aka Tier-1-Applications ) the recommendation is to avoid over commitment of processor resources (maintain a 1:1 ratio of physical cores to vCPUs).&lt;/li&gt;
&lt;/ul&gt;

A best practice for virtual machines is to right-size them to the amount of CPU and memory that is needed, and to keep the NUMA node boundaries in mind when doing this sizing. In other words, assign the amount of vCPUs and memory that are needed for the VM, but limit this to what is really needed. If the size of the VM can be kept to equal or less than the size of the NUMA node, then performance can be optimized, as remote NUMA node memory access is limited or eliminated. This aspect of NUMA performance was measured on the 4-socket (or 4 NUMA node) Broadwell server. Four 24-vCPU virtual machines were tested and compared against the performance of a single 96-vCPU VM. The results in the chart below show that the total throughput of the four VMs was 15% higher than the throughput of the single VM (see &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/performance/vsphere65-oracle-monster-vm-perf.pdf&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).

&lt;h3&gt;Enable hyperthreading&lt;/h3&gt;
Hyperthreading enables a single physical processor core to behave like two logical processors, allowing two independent threads to run simultaneously. Unlike having twice as many processor cores which can roughly double performance hyperthreading can provide anywhere from a slight to a significant increase (up to 24 percent) in system performance by keeping the processor pipeline busier.
&lt;br/&gt;&lt;br/&gt;
Avoid CPU affinity on systems with Hyperthreading. Pinning vCPUs from different/single SMP VMs to both logical processors on one core causes poor performance because logical processors share processor resources and that will affect the ability of the NUMA scheduler to rebalance VMs across NUMA nodes for fairness.

&lt;h3&gt;Keep VM size to within the NUMA node&lt;/h3&gt;
Wide VMs are defined as VMs with more vCPUs than the number of cores in each physical NUMA node are referred to as wide VMs. Because vCPUs in these wide VMs might need to access memory outside their own NUMA node, they might experience higher average memory access latencies than virtual machines that fit entirely within a NUMA node. The recommendation is to keep the default of one core per socket (with the number of virtual sockets therefore equal to the number of vCPUs) for vNUMA to match physical NUMA topology. Try to align VMs with physical NUMA boundaries.

&lt;h3&gt;Number of virtual sockets versus number of cores per socket&lt;/h3&gt;
In the blog post &lt;a href=&quot;https://blogs.vmware.com/vsphere/2013/10/does-corespersocket-affect-performance.html&quot; target=&quot;_blank&quot;&gt;Does corespersocket Affect Performance?&lt;/a&gt;, a test is presented in which a number of different set-ups are compared. The following items are concluded:
&lt;ul&gt;
    &lt;li&gt;When creating a virtual machine, by default, vSphere will create as many virtual sockets as we have requested vCPUs and the cores per socket is equal to one. We think of this configuration as &#39;wide&#39; and &#39;flat&#39;. This will enable vNUMA to select and present the best virtual NUMA topology to the guest operating system, which will be optimal on the underlying physical topology.&lt;/li&gt;
    &lt;li&gt;When we must change the cores per socket though, commonly due to licensing constraints, ensure we mirror physical server&#39;s NUMA topology. This is because when a virtual machine is no longer configured by default as &#39;wide&#39; and &#39;flat&#39;, vNUMA will not automatically pick the best NUMA configuration based on the physical server, but will instead honor wer configuration - right or wrong - potentially leading to a topology mismatch that does affect performance.&lt;/li&gt;
&lt;/ul&gt;

Note that if vNUMA is configured with vCPU hotplug settings, the virtual machine will be started without vNUMA and instead it will use Uniform Memory Access with interleaved memory access (see &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;cmd=displayKC&amp;externalId=2040375&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).

&lt;h2&gt;Memory&lt;/h2&gt;

&lt;h3&gt;Set memory reservations to avoid over-commitment, provide available physical memory&lt;/h3&gt;
&lt;ul&gt;
    &lt;li&gt;Size memory based on NUMA node size. Overallocation of memory to a virtual machine beyond what is needed can result in crossing physical NUMA node boundaries unnecessarily. Assign the needed memory, but do not over allocate, for example,
        &lt;ul&gt;
            &lt;li&gt;Oracle Database: Set memory reservations equal to the sum total of the size of the Oracle SGA, the Oracle PGA, the Oracle Background processes stack space and Operating System Used Memory. The memory reservation should be large enough to avoid kernel swapping between ESX and the guest OS because Oracle databases can be memory-intensive.&lt;/li&gt;
            &lt;li&gt;Oracle WebLogic Server: Set memory reservations equal to the sum of Xmx + 0.5 Xmx (where Xmx is the maximum heap size of each Java Virtual Machine running on the VM) and Operating System Used Memory (see &lt;a href=&quot;http://middlewaresnippets.blogspot.nl/2012/07/weblogic-and-virtual-machine-sizing.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Use HugePages&lt;/h3&gt;
The use of huge pages results in reduced memory management overhead and can increase hypervisor performance. Huge pages (2MB insteas of 4kB memory pages) improve performance by significantly reducing TLB misses (applications with large active memory working sets).

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
memory_to_reserve_in_mega_bytes = ...
number_of_huge_pages = memory_to_reserve_in_mega_bytes / 2
mem_lock = 2048 * (memory_to_reserve_in_mega_bytes / 2)


In /etc/sysctl.conf add the following lines

# The percentage of how aggressively memory pages are swapped to disk
vm.swappiness = 0
# The percentage of main memory the pdflush daemon should write data out to the disk.
vm.dirty_background_ratio = 25
# The percentage of main memory the actual disk writes will take place.
vm.dirty_ratio = 20
# set the number of huge pages based on the Hugepagesize, i.e., 2048kB
vm.nr_hugepages = ${number_of_huge_pages}

# give permission to the group that runs the process to access the shared memory segment
# to this end open the /etc/group file and retrieve the group-id
# when the oracle standard is used, the (oinstall) group id will be 54321
vm.hugetlb_shm_group = 54321


In /etc/security/limits.conf add the following lines

# memlock - maximum locked in-memory address space (kB)
@oinstall soft memlock ${mem_lock}
@oinstall hard memlock ${mem_lock}


When using JRockit we have to make a hugetblfs file system available in the directory /mnt/hugepages. 

mkdir -p /mnt/hugepages
chmod -R 777 /mnt/hugepages

In /etc/fstab add the following lines

nodev    /mnt/hugepages    hugetlbfs    defaults,mode=777    0 0
&lt;/pre&gt;

&lt;h2&gt;Networking&lt;/h2&gt;

&lt;h3&gt;Separate management traffic&lt;/h3&gt;
Virtual machines should not see infrastructure traffic (security violation) and should not be impacted by infrastructure traffic bursts (for example, vSphere vMotion operations).

&lt;h3&gt;Allocate at least 2 NICs per vSwitch to leverage NIC teaming capabilities&lt;/h3&gt;
NIC teams can share the load of traffic among some or all of its members, or provide passive failover in the event of a hardware failure or a network outage.

&lt;h3&gt;Use the VMXNET3 para-virtualized NIC&lt;/h3&gt;
The paravirtualized network adapters in the VMXNET family implement an optimized network interface that passes network traffic between the virtual machine and the physical network interface cards with minimal overhead.

&lt;h3&gt;Use network I/O control to converge network and storage traffic onto 10GbE&lt;/h3&gt;
This can reduce cabling requirements, simplify management, and reduce cost.

&lt;h2&gt;Storage&lt;/h2&gt;
&lt;ul&gt;
    &lt;li&gt;Create dedicated datastores to service database workloads. The creation of dedicated datastores for I/O-intensive databases is analogous to provisioning dedicated LUNs in the physical world. This is a typical design for a mission-critical enterprise workload, for example,
        &lt;ul&gt;
            &lt;li&gt;Oracle Database: Use dedicated LUNs or datastores for data and log virtual disks. Ensure that the storage being used for the data and log virtual disks is not shared with other virtual machines or applications. Use paravirtualized SCSI adapters for Oracle datafiles with demanding workloads. The combination of the new paravirtualized SCSI driver (PVSCSI) and additional ESX/ESXi kernel-level storage stack optimizations dramatically improves storage I/O performance.&lt;/li&gt;
            &lt;li&gt;Oracle WebLogic Server and then specifically &lt;a href=&quot;https://docs.oracle.com/middleware/12212/wls/PERFM/storetune.htm#PERFM231&quot; target=&quot;_blank&quot;&gt;FileStores&lt;/a&gt;:
                &lt;ul&gt;
                    &lt;li&gt;For basic (non-RAID) disk hardware, consider dedicating one disk per file store. A store can operate up to four to five times faster if it does not have to compete with any other store on the disk. Remember to consider the existence of the default file store in addition to each configured store and a JMS paging store for each JMS server.&lt;/li&gt;
                    &lt;li&gt;If disk performance continues to be a bottleneck, consider purchasing disk or RAID controller hardware that has a built-in write-back cache. These caches significantly improve performance by temporarily storing persistent data in volatile memory. To ensure transactionally safe write-back caches, they must be protected against power outages, host machine failure, and operating system failure. Typically, such protection is provided by a battery-backed write-back cache.&lt;/li&gt;
                &lt;/ul&gt;
            &lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

In an older &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/solutions/oracle/oracle-databases-vmware-workload-characterization-study.pdf&quot; target=&quot;_blank&quot;&gt;document&lt;/a&gt;, an example for a storage lay-out is given for the Oracle Database:

&lt;pre class=&quot;prettyprint linenums&quot;&gt;
LUN 1 – 8 (1TB each – RAID 5) -&gt; DATA: datastore 1 – 8 VMFS -&gt; 8 x VMDKs (900GB each)
LUN 9 - 10 (1.5TB each – RAID 10) -&gt; REDO: datastore 9 - 10 VMFS -&gt; 2 x VMDKs (1TB each)
LUN 11 (1.5TB – RAID 10) -&gt; VMORA: datastore 11 VMFS -&gt; 2 VMDK (30GB for OS, 50GB for Binary) 
&lt;/pre&gt;

In the case of Oracle WebLogic Server, it is in general not necessary to use multiple datastores, i.e., most &lt;a href=&quot;https://www.hpe.com/h20195/v2/getpdf.aspx/4aa3-3516enw.pdf&quot; target=&quot;_blank&quot;&gt;storage systems&lt;/a&gt; have controller hardware and protected write caches:

&lt;ul&gt;
    &lt;li&gt;Write cache is mirrored across controllers, and the system offers RAID 1+0 (mirroring + striping), RAID 5+0 (RAID 5 distributed parity + striping), and RAID MP (multiple distributed parity with striping) across the drives behind the nodes. In addition, controller nodes are configured in logical pairs whereby each node has a partner. The partner nodes have redundant physical connections to the subset of physical drives owned by the node pair. Within the pair, each serves as the backup node for the LDs owned by the partner node. If a controller node were to fail, data availability would be unaffected. In the event of a node failure, that node’s partner takes over the LDs for the failed node. It then immediately flushes data in the write cache on other nodes in the array that belongs on the LDs it has taken over.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Use paravirtualized SCSI adapters&lt;/h3&gt;
The combination of the new paravirtualized SCSI driver (PVSCSI) and additional ESX/ESXi kernel-level storage stack optimizations dramatically improves storage I/O performance.

&lt;h3&gt;Create dedicated datastores to service database workloads&lt;/h3&gt;
The creation of dedicated datastores for I/O-intensive databases is analogous to provisioning dedicated LUNs in the physical world. This is a typical design for a mission-critical enterprise workload.

&lt;h3&gt;Use multiple vSCSI adapters and evenly distribute target devices&lt;/h3&gt;
When using PVSCSI adapters we have 254 OIPS: &quot;The large-scale workloads with intensive I/O patterns require adapter queue depths greater than the Paravirtual SCSI (PVSCSI) default values. Current PVSCSI queue depth default values are 64 (for device) and 254 (for adapter). We can increase PVSCSI queue depths to 254 (for device) and 1024 (for adapter) inside a Windows virtual machine or Linux virtual machine (see &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;cmd=displayKC&amp;externalId=2053145&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for more information).&quot; In the case of &lt;a href=&quot;https://blogs.vmware.com/vsphere/2014/02/vscsi-controller-choose-performance.html&quot; target=&quot;_blank&quot;&gt;parallelism&lt;/a&gt;  &quot;It&#39;s worth noting that we can configure a total of 4 vSCSI adapters per virtual machine. To provide the best performance, one should also distribute virtual disks across as many vSCSI adapters as possible. This configuration provides the capability to process more IO simultaneously and benefit from additional queues if necessary (see &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/1M-iops-perf-vsphere5.pdf&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt; for an example - additional information can be found &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;cmd=displayKC&amp;externalId=1002598&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;)&quot;.

&lt;h3&gt;Use vSphere VMFS&lt;/h3&gt;
To balance performance and manageability in a virtual environment use &lt;a href=&quot;https://docs.vmware.com/en/VMware-vSphere/6.5/com.vmware.vsphere.storage.doc/GUID-5EE84941-366D-4D37-8B7B-767D08928888.html&quot; target=&quot;_blank&quot;&gt;VMFS&lt;/a&gt;. To store virtual disks, ESX/ESXi uses datastores, which are logical containers that hide specifics of physical storage from virtual machines and provide a uniform model for storing virtual machine files. Datastores that we deploy on block storage devices use the vSphere VMFS format, a special high-performance file system format that is optimized for storing virtual machines.

&lt;h3&gt;Align VMFS properly&lt;/h3&gt;
Like other disk-based file systems, VMFS suffers a penalty when the partition is unaligned. Use VMware vCenter to create VMFS partitions because it automatically aligns the partitions. In an older &lt;a href=&quot;https://www.vmware.com/pdf/esx3_partition_align.pdf&quot; target=&quot;_blank&quot;&gt;document&lt;/a&gt; some recommendations are given.

&lt;h2&gt;VM placement&lt;/h2&gt;
When a VM is being powered up in a DRS cluster, DRS runs its algorithm to determine the right ESXi host for it to be powered up on. This decision, also known as VM placement (or initial placement) is made based on the expected change in resource distribution (after ensuring that there are no constraint violations if the VM was placed on the host). One of the first steps in ensuring good VM performance is to make sure that the VM gets all the resources it needs as soon as it is powered on. DRS considers the demand of a VM, so it will never be short of resources whenever it is started.
&lt;br/&gt;&lt;br/&gt;
Apart from the fact that &quot;DRS strives to keep out VMs happy, we also need to keep in mind Oracle WebLogic Server &lt;a href=&quot;https://docs.oracle.com/middleware/12213/wls/CLUST/overview.htm#CLUST114&quot; target=&quot;_blank&quot;&gt;Clustering&lt;/a&gt;, i.e., making sure server in a cluster are placed on different VMs when necessary. So usually, we have to create a number of &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/techpaper/vsphere6-drs-perf.pdf&quot; target=&quot;_blank&quot;&gt;rules&lt;/a&gt;:

&lt;ul&gt;
    &lt;li&gt;VM/Host Rules:
        &lt;ul&gt;
            &lt;li&gt;Keep Virtual Machines Together (VM-VM) - This rule ensures that the VMs specified in the rule are always running on the same host.&lt;/li&gt;
            &lt;li&gt;Separate Virtual Machines (VM-VM) - This rule will keep the VMs in the rule always running on different hosts.&lt;/li&gt;
            &lt;li&gt;Virtual Machines to Hosts (VM-Host) - This type of rule is set on groups of one or more VMs and one or more hosts.&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/li&gt;
&lt;/ul&gt;

And last but not least: &quot;Start with a thorough understanding of your workload&quot;.

&lt;h2&gt;References&lt;/h2&gt;
[1] &lt;a href=&quot;https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/solutions/vmware-oracle-databases-on-vmware-best-practices-guide.pdf&quot; target=&quot;_blank&quot;&gt;Oracle Databases on VMware Best Practices Guide&lt;/a&gt;.&lt;/br&gt;
[2] &lt;a href=&quot;https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;cmd=displayKC&amp;externalId=2001003&quot; target=&quot;_blank&quot;&gt;Troubleshooting ESX/ESXi virtual machine performance issues (2001003)&lt;/a&gt;.&lt;/br&gt;
[3] &lt;a href=&quot;https://docs.vmware.com/en/VMware-vSphere/6.5/com.vmware.vsphere.monitoring.doc/GUID-8B77A187-40E0-4BA0-8E29-F8A83F33CDAD.html&quot; target=&quot;_blank&quot;&gt;Overview Performance Charts Disk Requests (Number)&lt;/a&gt;.</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/5562232150285579251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4896087204075281708/posts/default/5562232150285579251'/><link rel='alternate' type='text/html' href='http://middlewaresnippets.blogspot.com/2017/09/fun-with-virtualization.html' title='Fun with Virtualization'/><author><name>René van Wijk</name><uri>http://www.blogger.com/profile/16183092706195705923</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>