IBM Support

Using new features of Intelligent Management for Web Servers with Liberty collectives

Product Documentation


Abstract

Describes how to use the Intelligent Management for Web Servers feature of the Web Server Plug-ins for IBM WebSphere Application Server to route to the same application in multiple Liberty collectives, and how to use routing rules to route to a subset of available Liberty servers..

Content

Using new features of Intelligent Management for Web Servers with Liberty collectives

Abstract:

Before you begin:

Important: The interim fix for Intelligent Management in the web server plug-in for WebSphere Application Server APAR number PI73118 is necessary to route to the same application in multiple Liberty collectives, and to use routing rules. The collective controllers in the Liberty collective must be at version 16.0.0.4 or later to take advantage of these new functions of Intelligent Management for Web Servers.



You must install a web server that is supported by the Intelligent Management for Web Servers feature of the web server plug-in for WebSphere Application Server, such as the IBM HTTP Server. Install a 9.0.0.2 or higher web server plug-in for WebSphere Application Server, then install interim fix PI73118 before trying to route to the same application in multiple Liberty collectives or trying to use routing rules.
    1. Download and install the latest version of the IBM Installation Manager. For more information on installing IBM Installation Manager, see Installing Installation Manager and preparing to install the product.
    2. Use Installation Manager to access online product repositories to install the Web Server Plug-in for WebSphere Application Server and the needed interim fix for the Dynamic Routing feature.
    3. Use the Install wizard to install both the web server plug-ins and the required interim fix.
Concepts for routing to multiple collectives:

Intelligent Management for Web Servers enables routing of HTTP requests to members of Liberty collectives without requiring an administrator to regenerate the WebSphere plug-in configuration file when the environment changes. When servers, cluster members, applications, or virtual hosts are added, removed, started, stopped, or modified; the new information is dynamically delivered to the WebSphere plug-in. Requests are routed based on up-to-date information.

Intelligent Management for Web Servers routes application requests to all application instances when the application is deployed in multiple collectives. Previously, when routing to multiple collectives, a particular application could exist in only one of the collectives.

To use Intelligent Management for Web Servers to route HTTP requests to Liberty collectives, you enable the dynamicRouting-1.0 feature in all collective controllers of the collectives. The dynamicRouting-1.0 feature provides a Dynamic Routing service that delivers routing information to Intelligent Management for Web Servers. The dynamicRouting-1.0 feature also provides the dynamicRouting command. Use the dynamicRouting command’s setup, genPluginCfg, and genKeystore command actions to generate the keystores that are needed for secure communication between the plug-in and the Dynamic Routing service, as well as a plug-in configuration file that enables Intelligent Management for Web Servers in the WebSphere plug-in.

Important: To route to the same application in multiple collectives, each collective must have a unique name. The unique name of a collective can be specified in one of two ways:

    1. Use the connectorClusterName attribute of the <dynamicRouting> XML element in the server.xml of the collective controllers. All controllers in the same collective must use the same value for the connectorClusterName attribute. Controllers in different collectives must use different values for the connectorClusterName attribute. If the connectorClusterName attribute is specified, the value overrides the value specified with the –collectiveName option used when the collective was created.

    2. Use the --collectiveName option when the collective is created with the collective create command.

Procedure to enable routing to the same application in multiple collectives

    1. Enable Dynamic Routing in a controller by adding the following code to the featureManager tag in the server.xml of the controller.

    <feature>dynamicRouting-1.0</feature>
    2. Optional: Add the <dynamicRouting> element to the controller server.xml to specify properties for dynamic routing.

      The connectorClusterName property specifies the name that dynamic routing associates with this collective. If the connectorClusterName property is not specified, the name of the collective is used.

      For example:


      In the first collective, specify the following on all of the controllers:

      <dynamicRouting connectorClusterName="collective1"/>

      In the second collective, specify the following on all of the controllers:

      <dynamicRouting connectorClusterName="collective2"/>
    3. Start all controllers that have the Dynamic Routing feature enabled.

    4. Run the dynamicRouting setup command on one of the controllers to generate the keystore and plug-in configuration files.


      To create the artifacts that are needed for multiple-collective dynamic routing, use the --collectives option, instead of the --port, --host, --user, and --password options. Specify collectives in the collective_user:user_password@collective_host:port format with a comma (,) separating each collective. For example:

      ./dynamicRouting setup --collectives user1:password1@host1:port1,user2:password2@host2:port2
      --keystorePassword=webAS --pluginInstallRoot=/opt/IBM/WebSphere/Plugins/ --webServerNames=webserver1
      Ensure that each collective has a unique name. Also, ensure that the users exist in the user registries of their collectives and have an administrative role. If a password is not specified, you will be prompted for it.

      For more information about the dynamicRouting setup command, see Dynamic routing command.


    5. Copy the generated plugin-key.jks and plugin-cfg.xml files to a temporary directory on the web server host.

      For the --collectives option, multiple keystore files are created:

        o plugin-key.jks allows collective member servers in all collectives to accept requests that are proxied through web server.
        o plugin-key-collective_name.jks provides a plug-in key file for each collective specified with the --collectives option.

      These files enable the WebSphere plug-in to communicate securely with the collective controllers in the specific collective. Copy all the generated plugin-key*.jks files and the plugin-cfg.xml file to a temporary directory on the web server host.

    6. On the web server host, run gskcmd to convert the keystore to CMS format and to set personal certificate as the default. CMS format is the supported format of the WebSphere plug-in.

      For example:

      gskcmd -keydb -convert -pw <password> -db /tmp/plugin-key.jks -old_format jks -target /tmp/plugin-key.kdb -new_format cms -stash
      gskcmd -cert -setdefault -pw <password> -db /tmp/plugin-key.kdb -label default
      Run the gskcmd on all the generated plugin-key*.jks files. Change the -db and -target options to specify each file.

      For z/OS, see Conversion of the keystore to CMS format on z/OS.


    7. Copy all the plugin-key.kdb, and plugin-key.sth files that are created by gskcmd from the temporary directory to the --pluginInstallRootargument_value/config/web_server_name/ directory.

    8. Copy the plugin-cfg.xml file to the directory specified in the WebSpherePluginConfig directive in the IHS httpd.conf file.



      The plugin-cfg.xml file is generated with the <IntelligentManagement> stanza. When Dynamic Routing is enabled in a collective, the file has one <Connector> stanza for each collective controller. For multiple-collective routing, each collective has one <ConnectorCluster> stanza. For example:

      <Property Name="Keyfile" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.kdb"/>
        <Property Name="Stashfile" Value="/opt/IBM/WebSphere/Plugins/config/webserver1/plugin-key.sth"/>
      <IntelligentMangement>
        <Property name="webserverName" value="webserver1"/>
        <Property name="RoutingRulesConnectorClusterName" value="collective1"/>
       <ConnectorCluster enabled="true" maxRetries="-1" name="collective1" retryInterval="60">
        <Property name="uri" value="/ibm/api/dynamicRouting"/>
        <Connector host="controller1.acme.com" port="9444" protocol="https">
        <Property name="keyring" value="/opt/HTTPServer_Plugins/config/webserver1/plugin-key-collective1.kdb"/>
        </Connector>
       </ConnectorCluster>
      <ConnectorCluster enabled="true" maxRetries="-1" name="collective2" retryInterval="60">
       <Property name="uri" value="/ibm/api/dynamicRouting"/>
       <Connector host="controller2.acme.com" port="9444" protocol="https">
        <Property name="keyring" value="/opt/HTTPServer_Plugins/config/webserver1/plugin-key-collective2.kdb"/>
        </Connector>
       </ConnectorCluster>
      </IntelligentManagement>
    9. Start the web server and begin routing to the applications installed in the collectives.

Concepts for routing to a subset of available servers using routing rules

By default, dynamic routing load balances requests across all servers that can handle the request. To override the default behavior, you must configure routing rules. Routing rules can route requests to specific server resources, redirect requests, or reject requests.

You can use routing rules in Liberty dynamic routing to customize exactly which servers will be used to handle specific requests.

Examples:


    1. If you deploy the same application to two different clusters, you can use routing rules to direct requests from a particular set of client IP addresses to one of the clusters and the rest of the requests to the other cluster.

    2. If you deploy the same application in multiple collectives, you can use routing rules to send the requests only to the first collective, unless no servers are available in the first collective, then send requests to servers in the second collective.


Important: When routing to multiple collectives, routing rules can be specified in only one of the collectives. Intelligent Management for Web Servers will read routing rules only from the collective specified by the RoutingRulesConnectorClusterName property of the <IntelligentManagement> stanza in the plugin-cfg.xml file. The value of the RoutingRulesConnectorClusterName property is initialized with the name of the first collective specified by the --collectives option of the dynamicRouting command that created the plugin-cfg.xml file. Make sure you add your routing rules to the collective controllers in the collective specified by the RoutingRulesConnectorClusterName property of the <IntelligentManagement> stanza in the plugin-cfg.xml file.


Match expression and actions

Routing rules provide a match expression and an action. The match expression is applied to each request. When a request matches the match expression, the action specified by the rule is performed for that request. Match expressions examine various properties of the request such as the URI, headers, cookies, parameters, and client IP address. The action taken is to either reject, redirect, or permit the request.

Routing rules have an order number assigned to each rule. The rules are evaluated from lowest order number to highest. The first rule that matches a request determines how the request is handled. If no rule is matched, the request is load-balanced across all available servers that can handle the request.

If the action type of a rule is to reject the request, then the applicable HTTP return code is specified. The reject code must be one of the rejection codes supported by the web server.

If the action type of a rule is to redirect the request, then the redirection location is specified.

If the action type of a rule is to permit the request, the destinations where the request can be sent are specified. The destinations specify the set of all servers to choose when load balancing the request. From the set of destinations, only the servers that can best handle the request are used. Failover destinations can also be specified. Servers in a failover destination are only used if all servers in a primary destination are unavailable. Destinations can be specified as either clusters or servers.

By default, when a request has session affinity, server selection is based on affinity. If an affinity server is found, routing rules are not used. To allow routing rules to override affinity selection, the overrideAffinity attribute can be added to the <routingRules> element of server.xml.


Cluster destination

A cluster destination is specified with a collective name and a cluster name. Either part of the cluster destination can use wildcard (*) characters. For example, if a cluster destination is specified as cluster=collective1,*, then servers in any cluster in collective1 can be used. If a cluster destination is specified as cluster=*,cluster1, then servers in cluster1 in any collective can be used.


Server destination

A server destination is specified with a collective name, a host name, a user directory, and a server name. All parts of the server destination can use wildcard characters. For example, if a server destination is specified as server=collective1,*,*,*, then any servers in collective1 can be used. If a server destination is specified as server=*,*,*,server1, then server1 in any collective can be used.


How Routing Rules are evaluated

Before any routing rules are applied, Intelligent Management for Web Servers determines the best set of destinations to serve a request. The best set of destinations are those servers with web applications whose virtual host, context root, and servlet mappings best match the request. Routing rules can restrict the destinations used for routing to a subset of the full set of best destinations. Routing rules cannot cause a destination to be chosen that is outside of the original set of best destinations.

Example:


    ApplicationA has context root /A/* and is installed on clusterA.

    ApplicationAB has context root /A/B/* and is installed on clusterAB.

    Both applications can serve the request /A/B/myservlet. However, since /A/B/* context root is a better match for /A/B/myservlet than /A/* context root, requests for /A/B/myservlet will always be routed to clusterAB.

    A routing rule that matches a request for /A/B/myservlet can be used to restrict destinations to a subset of servers in clusterAB, but cannot be used to select servers in clusterA, since clusterA will never be chosen as a match for this request.



Procedure to route to a subset of available servers using routing rules

Avoid problems: All collective controllers in the collective must provide the same set of routing rules through the Dynamic routing service. Ensure that all controllers use the same routing rules by adding an xml file containing the routing rules to the configDropins/defaults directory of one of the controllers. See Automatically sharing configurations among replicas.

1. Add the <routingRules> element as a child of the <dynamicRouting> element in the controller server.xml file.


    a. Optionally, specify the webServers attribute of the <routingRules> element. Each <routingRules> element can define the applicable set of web servers where the rules will be published. When a web server connects to the DynamicRouting service, the service delivers rules to that web server. If you specify no web servers, the service delivers rules to all web servers that connect. If you specify more than one web server, use a comma (,) as the delimiter. Any rules specified with a webServers attribute value of “*” will be delivered to all web servers.

    Example:

    <dynamicRouting>
        <routingRules webServers="webserver1,webserver2">
            ...
        </routingRules>
    </dynamicRouting>

    b. Optionally specify the overrideAffinity attribute of the <routingRules> element. By default, a request that has affinity to a particular server will be sent to that server, even if the request matches a routing rule that does not contain the affinity server as a destination. If the overrideAffinity property is set to “true”, then selecting a destination that is in a matched rule will take precedence over selecting the affinity server. If the affinity server satisfies the matched rule, the affinity server will be chosen, even if the affinity server is in a failover set of endpoints.
    The overrideAffinity property applies to all routing rules, the overrideAffinity property cannot be set on a per rule basis.
    Example:


    <dynamicRouting>
      <routingRules webServers="webserver1" overrideAffinity=”true”>
            ...
      </routingRules>
    </dynamicRouting>
2. Add <routingRule> elements as children of the <routingRules> element.

    a. Specify an order attribute for the routing rule and set it to an integer. The order attribute is required. Rules are evaluated from lowest order to highest. If more than one rule is assigned the same order attribute, the first rule found is published and all other rules with the same order attribute are discarded. A good practice is to leave gaps between the order numbers of the rules to provide space for future rules.

    b. Specify a matchExpression attribute for the routing rule. Set it to an expression that can find matching incoming requests. Match expressions combine a set of fixed operands with operators. You can combine sets of operands and operators by using AND and OR operators with parenthesis. You can negate a set of operands and operators using the NOT operator.

    Example:

    <dynamicRouting>
        <routingRules webServers="webserver1,webserver2">
          <routingRule order="100" matchExpression="URI LIKE'/myapp%'">
                ...
          </routingRule>
        </routingRules>
    </dynamicRouting>

    Table 1. matchExpression operands. Include an operand in a matchExpression definition.
    OperandSyntaxDescription
    Client IPV4clientipv4The IP address of the client that uses the Internet Protocol version 4 (IPv4) dotted quad address type of n.n.n.n.
    Client IPV6clientipv6The Internet Protocol version 6 (IPv6) 128-bit address type of x:x:x:x:x:x:x:x that follows Request for Comments 1924 (RFC 1924) of the client computer.
    Cookie namecookie$nameA cookie name.

    For example, the expression cookie$My_Cookie_Name='My_Cookie_Value' tests a request to see if it contains a cookie named My_Cookie_Name with a My_Cookie_Value value. To test for the presence or absence of a particular cookie, use one of the following expressions:

    cookie$MyCookieName IS NOT NULL


    cookie$MyCookieName IS NULL
    Header nameheader$nameA header name and value.

    For example, the expression header$Host='localhost' tests a request to see if it contains an HTTP host header with a locahost value. To test for presence or absence of the host header, use one of the following expressions:

    header$Host IS NOT NULL


    header$Host IS NULL
    Percentagepercentage$valThe percentage operand evaluates to true some percentage of the time.

    For example, percentage$50 evaluates to true on average 50% of the time.

    Query parameterqueryparm$nameA header name and value.

    For example, the expression queryparm$timezone='EST' tests a request to see if the request contains an HTTP query parameter named timezone with an EST value. To test for the presence or absence of a query parameter, use one of the following forms:

    queryparm$timezone IS NOT NULL


    queryparm$timezone IS NULL
    URIuriUniform Resource Identifier
    Virtual hostvirtualhostVirtual host target of the request
    Virtual portvirtualportVirtual port target of the request



    Table 2. matchExpression operators. Include an operator in a matchExpression definition as needed.
    OperatorSyntaxDescription
    Equals=The equality operator expresses a case-sensitive match.
    Equals ignore caseEQUALSIGNORECASEIdentical to 'String = String' except that the case of the strings is ignored. For example, 'ABC' EQUALSIGNORECASE 'abc' evaluates to true and ('ABC' = 'abc') evaluates to false.
    ININExpresses an operand with multiple values in a single expression. For example, for an operand called port, to express that the port value can be any or all of the integers 9080, 9090, and 9091, the expression fragment is port IN (9080,9090,9091).

    How the values inside the parentheses are expressed depends on the data type. For example, if port is an integer, the correct syntax is the value without quotation marks. If port is a string, the correct syntax is port IN ('9080','9090','9091').

    Is not nullIS NOT NULLEvaluates to true if the specified operand exists.
    Is nullIS NULLEvaluates to true if the specified operand does not exist.
    LikeLIKEExpresses pattern matching for string operand values. The value must contain the wildcard character percent sign (%) in the position where the pattern matching starts. For example:
        o The host LIKE %blanca expression matches the word blanca or any other word that ends in blanca.

        o The host LIKE blanca% expression matches the word blanca or any other word that starts with blanca.

        o The host LIKE %blanca% expression matches the word blanca or any word that contains blanca.

    Like ignore caseLIKEIGNORECASEIdentical to 'string LIKE string' except that the case of the strings is ignored.
    Like inLIKEINEvaluates whether a string exists in a list of strings. For example, string likein (string1%, string2%, string3%, etc.) evaluates to true if string matches one or more of the strings in the parentheses. In this example, the parentheses contain three string values.

3. Specify an action type for a routing rule.

The three possible action types are permitAction, redirectAction, or rejectAction. Specify only one of the three possible action types for each routing rule.


    o Specify the permitAction action type to route requests to identified endpoints.

      a. Add the <permitAction> element to a <routingRule> element.

      b. Optionally, add the allowMaintenanceModeServers attribute to the <permitAction> element to specify whether requests that match the rules are sent to servers in maintenance mode. The default value for the allowMaintenanceModeServers attribute is false. When this attribute is set to true, requests that match this rule can be sent to servers that are in maintenance mode.

      c. Add one or more <loadBalanceEndPoints> elements to the <permitAction> element.

      If a <permitAction> element has more than one <loadBalanceEndPoints> element, the first <loadBalanceEndPoints> element acts as the primary set of destinations. All subsequent <loadBalanceEndPoints> elements act as failover destinations. The order in which the <loadBalanceEndPoints> elements are defined determines the failover priority.

      d. Add one or more <endpoint> elements to each <loadBalanceEndPoints> element.

      e. Add a destination attribute to each <endpoint> element. Set the destination attributes to either a server type or a cluster type.

      To specify a server-typed endpoint, specify server= followed by a four-part server specification, with parts delimited by a comma:

      Example:



      <endpoint destination="server=collective_name,host_name,wlp.user.dir,server_name"/>
        • collective_name is the name of the collective where the server is a member.
        • host_name is the host address.
        • wlp.user.dir is the Liberty user directory on the host system where the server is defined. Note that you possibly can use the ${wlp.user.dir} variable here, but it might not be appropriate. When this server.xml is evaluated, the value of ${wlp.user.dir} is the directory where the controller server is defined. This might not be the same directory where the member server is defined.
        • server_name is the server name.
      To specify a cluster-typed endpoint, specify cluster= followed by a two-part cluster specification, with parts delimited by a comma:

      Example:



      <endpoint destination="cluster=collective_name,cluster_name"/>
        • collective_name is the name of the collective where the cluster is defined
        • cluster_name is the cluster name.

        All parts of a server or cluster destination specification can use the wildcard character (*) to match any string. To use routing rules to route to multiple collectives, each collective must have a different name. If the connectorClusterName attribute of the <dynamicRouting> element is specified, the collective name is the value of the connectorClusterName attribute. If the connectorClusterName attribute of <dynamicRouting> is not specified, the collective name is the value used for the --collectiveName option when running the collective create command. If the connectorClusterName attribute is not specified, and the --collectiveName option is not used, the collective name is defaultCollective.

      The following example shows a <permitAction> element with a server-typed endpoint:


        <dynamicRouting maxRetries="5" retryInterval="10000">
          <routingRules webServers="myWebServer">
            <routingRule order="100" matchExpression="URI LIKE '/myapp%'">
              <permitAction>
                <loadBalanceEndPoints>
                  <endpoint destination="server=collective1,myhost,/opt/IBM/liberty/wlp/usr,member1"/>
                </loadBalanceEndPoints>
              </permitAction>
            </routingRule>
          </routingRules>
        </dynamicRouting>
      o Specify the redirectAction action type to route a request to another location.

        a. Add the <redirectAction> element to a <routingRule> element.

        b. Add a location attribute to the <redirectAction> element. Set the location attribute to the new destination for the request.

        The following example shows a <redirectAction> element:

        <dynamicRouting maxRetries="5" retryInterval="10000">
          <routingRules webServers="myWebServer">
              <routingRule order="200" matchExpression="URI LIKE '/myapp%'">
                <redirectAction location="http: //some.other.destination" />
              </routingRule>
            </routingRules>
        </dynamicRouting>
    o Specify the rejectAction action type to reject a request with a specific response code.
        a. Add the <rejectAction> element to a <routingRule> element.

        b. Add a code attribute to the <rejectAction> element. Set the code attribute to the HTTP response code to use when the request matches the rule. The code must be an error code that the web server supports.

        The following example shows a <rejectAction> element:

        <dynamicRouting maxRetries="5" retryInterval="10000">
          <routingRules webServers="myWebServer">
            <routingRule order="300" matchExpression="URI LIKE '/myapp%'">
                <rejectAction code="503" />
            </routingRule>
          </routingRules>
        </dynamicRouting>

Results

After the rules are defined, when a web server connects to the dynamic routing service, the rules associated with that web server, as well as the rules associated with all web servers, are delivered. The web server matches requests against the match expressions found in the rules. The expressions are evaluated according to the rule order, from lowest order to highest. The first rule with a matching expression is used. The action associated with the rule determines how the request is handled. If no rules are matched, the request is load balanced across all servers that can handle the request.

By default, when a request has session affinity, server selection is based on affinity. If an affinity server is found, that server is chosen and the routing rules are not evaluated. To allow routing rules to override affinity selection, the overrideAffinity attribute can be added to the <routingRules> element of server.xml.


Example

The following example combines multiple rules:

<dynamicRouting>
      <routingRules webServers="myWebServer">
        <routingRule order="100" matchExpression=" uri like '/AppX%' ">
          <permitAction>
            <loadBalanceEndPoints>
              <endpoint destination="cluster=collective1,clusterA"/>
              <endpoint destination="cluster=collective2,clusterA"/>
            </loadBalanceEndPoints>
         </permitAction>
        </routingRule>
        <routingRule order="200" matchExpression="URI LIKE '/myapp%'">
          <permitAction>
            <loadBalanceEndPoints>
               <endpoint destination="server=collective1,*,*,*"/>
            </loadBalanceEndPoints>
            <loadBalanceEndPoints>
               <endpoint destination="server=collective2,*,*,*"/>
            </loadBalanceEndPoints>
          </permitAction>
        </routingRule>
        <routingRule order="300" matchExpression="uri like '/AppY%' AND queryparm$test = 'true'">
         <permitAction allowMaintenanceModeServers="true">
            <loadBalanceEndPoints>
              <endpoint destination="server=collective1,host1,*,server1"/>
            </loadBalanceEndPoints>
         </permitAction>
        </routingRule> 
        <routingRule order="400" matchExpression="URI LIKE '/myoldapp'">
          <redirectAction location="http://mysite/mynewapp" />
        </routingRule>
        <routingRule order="500" matchExpression="URI LIKE '/myveryoldapp'">
          <rejectAction code="503" />
        </routingRule>
      </routingRules>
</dynamicRouting>

The <routingRule> with order=”100” limits the destinations where requests for /AppX are routed to two particular clusters across two collectives.

The <routingRule> with order=”200” shows how to enable failover from one set of endpoints to another. Rule 200 routes all requests that match the expression to any servers that can handle the request in “collective1”. If all servers that can handle the request in “collective1” are unavailable, servers that can handle the request in “collective2” are chosen for requests that match the rule expression.

The <routingRule> with order=”300” shows an example of routing test requests to a server that is in maintenance mode. If “test=true” is added as a query parameter, then those requests will be sent to server1 in collective1, even if that server is in maintenance mode.

The <routingRule> with order=”400” shows an example of a redirect rule.

The <routingRule> with order=”500” shows an example of a reject rule.

Original Publication Date

14 December 2016

[{"Product":{"code":"SSEQTP","label":"WebSphere Application Server"},"Business Unit":{"code":"BU053","label":"Cloud & Data Platform"},"Component":"Liberty","Platform":[{"code":"PF002","label":"AIX"},{"code":"PF010","label":"HP-UX"},{"code":"PF016","label":"Linux"},{"code":"PF027","label":"Solaris"},{"code":"PF033","label":"Windows"},{"code":"PF035","label":"z\/OS"}],"Version":"9.0.0.2;16.0.0.4","Edition":"Network Deployment","Line of Business":{"code":"LOB45","label":"Automation"}}]

Document Information

Modified date:
17 June 2018

UID

swg27049232