oracle-otd

Copyright 2019 © LimePoint Pty Ltd. All rights reserved.

Introduction

This controller implements functionality for Oracle Traffic Director. Note that for floating IPs, if the OTD controller is not provided access to the root user, you will need to enable certain sysctl parameters to take advantage of floating IPs in high availability modes - please consult the OTD documentation as to how to ensure these are correctly configured for your environment (specifically, docs.oracle.com/middleware/1221/otd/admin/ha.htm#OTADG297 )

Versions

Supported Versions: 12.2.1.2, 12.2.1.3

Supported Configurations

Currently, the following configurations are supported: * Colocated with a full Weblogic Domain (this requires access to a database) * Colocated with a restricted JRF Weblogic Domain (this does not require access to a database)

Standalone mode is currently not fully supported.

How it works

The OTD controller wraps the provided API by oracle (documented at docs.oracle.com/middleware/12213/otd/OTWST/reference.htm , - knowledge of the OTD product is beyond the scope of this document, however fundamentally an OTD setup consists of a set of configurations, which contain items such as server pools and origin servers. You can see this tree by refering to the refernce documentation for the MintPress::OracleTrafficDirectory::Configuration class.

domain=MintPress::OracleWeblogic::Domain.new(name: 'otd_domain',                                                                                                                                                                                                                                                                                                                             admin_password: admin_password,
                                             root_directory: '/oracle/app/otd/environments',
                                             'admin_server.listen_port': 9090,
                                             'admin_server.ssl.listen_port': 9190,                                                                                                                                                                                                                                                                                                           restricted_jrf: true,                                                                                                                                                                                                                                                                                                                           templates: MintPress::OracleTrafficDirector::TemplateSet.new )

Note the ‘restricted_jrf: true’ setting, which causes OTD to not require access to a database server - most installations will desire this setup!

To create a simple configuration, the following ruby code would suffice:

# Create a configuration
domain.configuration['demo_otd'] = MintPress::OracleTrafficDirector::Configuration.new(listener_port: 19001, server_name: 'demo-otd.mintpress.io')

# Add an instance
domain.configuration['demo_otd'].add_instance('demootd-n1.mintpress.io')

# Add a server pool
domain.configuration['jj_otd'].add_origin_server_pool(name: 'obpsoa-ssl-origin-server-pool',
                                                      type: 'http',
                                                      'ssl.enabled': false,
                                                      'ssl.tls11': false,
                                                      'health_check.request_uri': '/soa-infra/services/isSoaServerReady',
                                                      'health_check.request_method': 'GET',
                                                      'health_check.interval': 4,
                                                      'health_check.timeout': 5,
                                                      'health_check.respond_code_match': 200)
# Add an origin server
domain.configuration['jj_otd'].origin_server_pool['obpsoa-ssl-origin-server-pool'].add_origin_server(name: 'obpsoa.demo.mintpress.io:8002')

# Add a virtual server
domain.configuration['jj_otd'].origin_server_pool['obpsoa-ssl-origin-server-pool'].add_virtual_server(name: 'obpsoa-e-virtual-server',
                                                                                                      host: 'obpsoa.mintpress.io',
                                                                                                      'access_log.format': '%Ses->client.ip% - %Req->vars.auth-user% [%SYSDATE%] \"%Req->reqpb.clf-request%\" %Req->srvhdrs.clf-status% %Req->srvhdrs.content-length% %Req->vars.ecid% %Req->vars.origin-server% %duration%',
                                                                                                      'perf_dump.uri': '/perf'
                                                                                                     )
# Run the create action to ensure the configuration!
domain.configuration['jj_otd'].create

To restart an instance , you can call #start and #stop on it (they are children of the Oracle Weblogic System Component class, and all methods apply to them as well)

domain.configuration['demo_otd'].add_instance('demootd-n1.mintpress.io').start
domain.configuration['demo_otd'].add_instance('demootd-n1.mintpress.io').stop

For examples of use with other integrations, such as Chef and Ansible, please see the included demo recipes.

About managing origin servers

It is now possible to drain the servers via the SDK, using the start_draining and disable_when_drained actions. The reason these are seperated, is to enable draining multiple origin servers at once - a fairly common situation with complex applications. If you have a JSON file for your OTD that you built with your runtime, the code can be as simple as this:

require 'mintpress-oldworld-integration'
require 'oracle-otd'

# We want to drain any origin servers pointing to this
shorthost = "origintest"

# Load the model in from a json file
json_data = JSON.parse("otd.json")
model = EnvironMintModelUtils::MintModel.new(json_data)
otd_model =  MintPress::Migration::ModelProcessor.new(model, installations_fatal: false, templates_fatal: false, host_options: host_options, generic_component_okay: true)

# Grab the OTD domain
otd_domain = otd_model.domains.values[0]

# Iterate through the configurations
draining_pools = []
otd_domain.otd_configuration.values.each do |otd_configuration|
  # Iterate through the origin server pools
  otd_configuration.origin_server_pool.values.each do |otd_origin_pool|
    # Lets iterate the OTD origin servers for any which match our shorthost
    otd_origin_pool.origin_server.values.each do |otd_origin_server|
      if otd_origin_server.host.start_with?(short_host)
        # Start this pool draining
        otd_origin_server.start_draining
        # Record that we did it
        draining_pools << otd_origin_server
        # Optionally, set a property to limit how long we drain for
        otd_origin_server.drain_timeout = 300
      end
    end
  end
end


# If you want to handle this manually, this is exactly what the below static function does
=begin
# Now disable pools as they become drained
draining_pools.each do |pool|
  draining_pools.disabled_when_drained
end

# Lets enable them again straight away...
draining_pools.each do |pool|
  draining_pools.enable
end
=end

MintPress::OracleTrafficDirector::OriginServer.drain_server_set(draining_pools, timeout: 300)

Alternatly, you can harvest your configuration from the OTD instead via code that looks like this:

# Grab a host of our admin servers
admin_host = Host.new(name: admin_host_name, connect_user: 'mintpress', final_user: 'oracle', keys: '~/.ssh/id_rsa')
# Grab a domain
domain = Domain.new(name: domain_name, host: admin_host, admin_user: 'weblogic', admin_password: 'welcome1', 'admin_server.listen_port': 7001)
# Inform the system that this is OTD - if you already know your configuration name, you can instead do:
#
# configuration = MintPress::OracleTrafficDirector::Configuration.new(name: config_name, domain: domain)
#
domain.templates << MintPress::OracleTrafficDirector::Template.new

# Harvest the domain on access
domain.harvest_on_access = true

# Iterate through configurations - accessing configuration.keys addresses a ruby
# quirk where `each` will sometimes happen without passing through to the harvest 
# path
puts "Iterating: #{domain.templates[-1].configuration.keys.inspect}"
domain.otd_configuration.values.each do |otd_config|
  puts "Working on #{otd_config.name}"
end

If you already know the configurations you’re going to work on, and just need to worry about the origin servers, the code can be simpler:

config_name = 'otd_test'
# Grab a host of our admin servers
admin_host = Host.new(name: admin_host_name, connect_user: 'mintpress', final_user: 'oracle', keys: '~/.ssh/id_rsa')
# Grab a domain
domain = Domain.new(name: domain_name, host: admin_host, admin_user: 'weblogic', admin_password: 'welcome1', 'admin_server.listen_port': 7001)
configuration = MintPress::OracleTrafficDirector::Configuration.new(name: config_name, domain: domain)

# Iterate through the configuration object as in above examples!

Handling multiple processes

Generally, it’s recommended to do all of your OTD manipulation from a single process, but if you have a requirement to run multiple processes in parallel, it’s important to synchronize them so they don’t grab the edit lock at the same time. For a small number, you can set the environment variable MINTPRESS_EXCLUSIVE_WLST to true, which will cause the WLST session to wait for any edit lock to be cleared before asserting its own.

This has a major disadvantage, however, that each of your processes are still running a WLST session. Each of these will take around 512mb of ram on your OTD server, which is obviously will cause problems if you run, say, 20 of them. A better approach is to lock the WLST with code like this:

# This function tests we have one and only one WLST open at any time
def ensure_wlst_available(origin)
  while origin.domain.adminserver_transport.execute("ps axww | grep -v grep | grep weblogic.WLST | wc -l").stdout.to_i > 0
    puts "Waiting for WLST session on OTD domain"
    # In case a harvest opened a session, ensure we close it.
    origin.domain.wlst_session.close if domain.has_open_wlst?
    sleep 10*rand()
  end
end

# This function waits for a WLST session to be drained
def wait_for_drain_only(origin, timeout: nil)
  while true
    # Get our connection count using a locked wlst, which we then immediatly close
    ensure_wlst_available(origin)
    # You can use different metrics here!
    connection_count = origin.get_metric(['countActiveConnections', 'countActiveStickyConnections', 'requests5MinuteAverage']).inject(0, :+)

    # If the connection count is 0, _or_ we have exceeded timeout, then we return
    if connection_count == 0 or (self.drain_timeout.nil? or TIme.now.to_i - @drain_start_time <= self.drain_timeout)
      origin_server.disable
      origin.domain.wlst_session.close
      return
    end
    sleep 30*rand()
    origin.domain.wlst_session.close
  end
end

# This function disables a set of origin servers
def disable_servers(origin_server_list)
  # Start all of our servers draining
  ensure_wlst_available(origin_server)
  origin_server_list.each do |origin_server|
    origin_server.start_draining
  end
  # Close the session so others can use it
  origin_server.domain.wlst_session.close

  # Now wait for drains to complete
  origin_server_list.each do |origin_server|
    wait_for_drain_only(origin_server, timeout: 300)
  end
end

# This function enables the origin servers
def enable_servers(origin_server_list)
  ensure_wlst_available(origin_server)
  origin_server_list.each do |origin_server|
    origin_server.enable
  end
  # Close the session so others can use it
  origin_server.domain.wlst_session.close
end

Threaded Failover Start

Right now, a performance issue can be waiting for sudo authentication for failover - this happens in serial today, due to a race condition experienced on a client site, hence while we believe this issue is resolved, it is disabled by default out of an abundance of caution. If you would like to enable this parallel functionality, you can set the environment variable OTD_THREADED_FAILOVER to true, or the ruby global variable $otd_threaded_failover = true.

License & Authors

  • Author:: LimePoint (support@limepoint.com)

# MintPress® - Automation and Configuration Management Platform
# Copyright © 2018 LimePoint. All rights reserved.
#
# This program and its contents are confidential and owned by LimePoint.
# Only licenced users are permitted to access and use of this file.
# This program (or any part of it) may not be disclosed, copied or used
# except as expressly permitted in LimePoint’s End User Licence Agreement.
#
# LimePoint® and MintPress® are Registered Trademarks of LimePoint
# For more information contact LimePoint at http://www.limepoint.com