Enable Tuxedo Domain Features with the DPK

Since the DPK was released, there has a been a bug (for Windows) that is quite annoying. In the psft_customizations.yaml file, the feature_settings: section is supposed to turn Tuxedo domain features on or off.

feature_settings:
  PUBSUB:           "Yes"
  QUICKSRV:         "No"
  QUERYSRV:         "Yes"
  JOLT:             "Yes"

On Windows, these settings were ignored and the default values were used when a domain was created or reconfigured. Thanks to Dale Haman from the psadmin.io Community, we have a fix for the bug. The issue is this:

The DPK (Puppet) takes the feature_settings: section in the psft_customizations.yaml file and combines it into a string. The string is passed into psadmin with the -u parameter. The code that creates this string used an incorrect separator for each feature, so psadmin would ignore the entire string.

To fix this, you can edit one file under the puppet/etc/modules folder:

puppet/etc/modules/pt_config/lib/puppet/provider/psftdomain.rb

if Facter.value(:osfamily) == 'windows'
  # feature_settings_separator = '#'
  # For Windows, we need to use the slash
  feature_settings_separator = '/'
else
  feature_settings_separator = '%'
end

If you re-run puppet apply .\site.pp, your app server and process scheduler domains features should match what you defined in psft_customizations.yaml.

Process Scheduler Features

After testing this on a process scheduler, I noticed my scheduler had 5 features listed in psadmin but only two were configured in my psft_customizations.yaml file: MSTRSRV and APPENG. I wanted to configure the other features in the domain, so I added them to my psft_customizations.yaml file:

feature_settings:
  MSTRSRV:          "Yes"
  APPENG:           "No"
  PPM:              "No"
  DOMAIN_GW:        "No"
  SERVER_EVENTS:    "No"

If you run puppet apply .\site.pp with these parameters in your psft_customizations.yaml file, you’ll get an error saying the Feature List is not valid. The feature_settings: section is compared to an array in the Process Scheduler Puppet Type to make sure you don’t mis-type anything, or try to add a non-existent feature. But in this case, the validation array was incorrect. It was missing the "PPM" feature.

puppet/etc/modules/pt_config/lib/puppet/type/pt_prcs_domain.rb

prcs_features =
  # [ "MSTRSRV", "APPENG", "KIOSK", "DOMAIN_GW", "SERVER_EVENTS" ]
  # Add PPM because it is a scheduler feature...KIOSK is not.
  [ "MSTRSRV", "APPENG", "PPM", "DOMAIN_GW", "SERVER_EVENTS"]

Comment out the bad line and change "KIOSK" to "PPM" in the array. Now, you can configure all 5 process scheduler features from the psft_customizations.yaml file.

Check out the other bug fixes for the DPK too:

Multiple PS_CFG_HOMEs with the DPK

One of my biggest criticisms of the DPK is that it doesn’t support multiple PS_CFG_HOME’s for app server and process scheduler domains. This has been the biggest roadblock for me as I attempt to use the DPK to deploy an 8.55 infrasturcture. Currently (as of 8.55.08), all Tuxedo domains are created under a single folder.

Tuxedo expects the PS_CFG_HOME environment variables to be set to know where to create domains. If you don’t set PS_CFG_HOME, Tuxedo’s default setting will create the domains under %USER%\psft\pt\8.xx.

With the DPK, there are parameters to set PS_CFG_HOME environment variable at the app server, process scheduler, and web server sections in psft_customizations.yaml. Use the ps_config_home: value to populate the value for all domains. Or, you can set different values for each domain using the ps_cfg_home_dir: inside the appserver_domain_list, prcs_domain_list, and pia_domain_list section.

When the DPK runs, it takes these values and sets up environment variables. If you have more than one PS_CFG_HOME specified for Tuxedo domains, the current DPK will only set environment variables once. The last PS_CFG_HOME specified will be the environment variable value when Tuxedo domains are created.

This is why the DPK only uses 1 PS_CFG_HOME for all Tuxedo domains; it doesn’t manage environment variables properly.

Setting PS_CFG_HOME for Every Domain

To fix this, we need to set the PS_CFG_HOME environment variable each time a Tuxedo domain is created (or booted). Puppet Providers handle the comamnd line calls to Tuxedo and setup programs. So a code change in the provider for Tuxedo domains where we need to make our change.

The Puppet Providers for Tuxedo are located under puppet\etc\modules\pt_config\lib\puppet\provider\. In the providers, when a command line command is built, a function execute_command() is used to handle the OS-level command line calls. Unfortunately, only the app server provider uses this function, because execute_command() takes two parameters: command and env. The env parameter lets us set environment variables for the command’s environment. Instead, we’ll use the Ruby syntaxt ENV[] set an environment variable before the command is executed.

App Servers

There are two base files for app server domains: psftdomain.rb and psftdomainboot.rb. They are parents objects to the pt_app_domain\app_domain.rb provider. The changes for application server domains can be made in the two parent files.

We’ll insert this code block before each command line call:

# PS_CFG_HOME Fix Begin 
if Facter.value(:osfamily) == 'windows' 
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir].gsub('/', '\\') 
else 
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir] 
end

ENV['PS_CFG_HOME'] = ps_cfg_home_dir_norm
Puppet.notice("     PS_CFG_HOME=#{ps_cfg_home_dir_norm}")
# PS_CFG_HOME Fix End

In psftdomain.rb, we’ll insert the code in the create() method before the begin code block:

def create pre_create()

  domain_name = resource[:domain_name]
  domain_type = get_domain_type()
  template_type = get_template_type()

  Puppet.debug("Creating domain: #{domain_name}")
  Puppet.debug("     with attributes #{resource.to_hash.inspect}")

  # PS_CFG_HOME Fix Begin
  if Facter.value(:osfamily) == 'windows'
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir].gsub('/', '\\')
  else
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir]
  end

  ENV['PS_CFG_HOME'] = ps_cfg_home_dir_norm
  Puppet.notice("     PS_CFG_HOME=#{ps_cfg_home_dir_norm}")
  # PS_CFG_HOME Fix End

  begin
    psadmin_cmd = File.join(resource[:ps_home_dir], 'appserv', 'psadmin')
    if Facter.value(:osfamily) == 'windows'

      command = "#{psadmin_cmd} #{domain_type} create -d #{domain_name} #{template_type} #{get_startup_settings} #{get_env_settings}"
      execute_command(command)
    else 
      domain_cmd('-m', '-s', '/bin/bash', '-l',  resource[:os_user], '-c',
                 "#{psadmin_cmd} #{domain_type} create -d #{domain_name} " +
                 "#{template_type} #{get_startup_settings} #{get_env_settings}")
    end

  rescue Puppet::ExecutionFailure => e
    raise Puppet::Error,
        "Unable to create domain #{domain_name}: #{e.message}"
  end

  post_create()
  @property_hash[:ensure] = :present

end

In psftdomainboot.rb, we’ll insert the code block in the execute_psadmin_action() method before the begin code block:

def execute_psadmin_action(action) 
  domain_name = resource[:domain_name] domain_type = get_domain_type()

  Puppet.debug("Performing action #{action} on domain #{domain_name}")

  # PS_CFG_HOME Fix Begin
  if Facter.value(:osfamily) == 'windows'
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir].gsub('/', '\\')
  else
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir]
  end

  ENV['PS_CFG_HOME'] = ps_cfg_home_dir_norm
  Puppet.notice("     PS_CFG_HOME=#{ps_cfg_home_dir_norm}")
  # PS_CFG_HOME Fix End

  begin
    psadmin_cmd = File.join(resource[:ps_home_dir], 'appserv', 'psadmin')
    if Facter.value(:osfamily) == 'windows'

      db_type = get_db_type()
      if db_type == 'ORACLE'
        set_user_env()
      end
      command = "#{psadmin_cmd} #{domain_type} #{action} -d #{domain_name}"
      command_output = execute_command(command)

    else
      os_user = resource[:os_user]
      if os_user_exists?(os_user) == false
        command_output="ERROR: os user #{os_user} does not exists"
      else
        command_output = domain_cmd('-m', '-s', '/bin/bash', '-',  os_user, '-c',
          "#{psadmin_cmd} #{domain_type} #{action} " + "-d #{domain_name}")
      end
    end
    return command_output

  rescue Puppet::ExecutionFailure => e
    raise Puppet::ExecutionFailure, "Unable to perform action #{action}: #{e.message}"
  end

end

Process Schedulers

For process scheduler domains, we need to modify the pt_prcs_domain\prcs_domain.rb provider file. We’ll insert the code block in the pre_create() method.

def pre_create 
  super()

  domain_name = resource[:domain_name]
  cfg_home_dir = resource[:ps_cfg_home_dir]

  # PS_CFG_HOME Fix Begin
  if Facter.value(:osfamily) == 'windows'
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir].gsub('/', '\\')
  else
    ps_cfg_home_dir_norm = resource[:ps_cfg_home_dir]
  end

  ENV['PS_CFG_HOME'] = ps_cfg_home_dir_norm
  Puppet.notice("     PS_CFG_HOME=#{ps_cfg_home_dir_norm}")
  # PS_CFG_HOME Fix End

  domain_dir = File.join(cfg_home_dir, 'appserv', 'prcs', domain_name)
  if File.exist?(domain_dir)
    Puppet.debug("Removing Process Scheduler domain directory: #{domain_dir}")
    FileUtils.rm_rf(domain_dir)
  end

end

Closing Thoughts

Fixing the limitation of one PS_CFG_HOME with the DPK was the last hurdle for me to start using the DPK for all of my environment builds. With the DPK, and some custom Puppet code to extend the DPK, we are working on automating our environment builds. I posted the code changes on GitHub if you want to get the fix from there.

I have an SR open for this issue, but I also have an Idea on the Oracle Community site. If you would like to see this change incorporated into the delivered DPK, go vote for the idea.