Chapter 8. The AdminConfig Object

“Architecture starts when you carefully put two bricks together.”

—Ludwig Mies van der Rohe

One of the most valuable things that wsadmin allows us to do is manipulate the AppServer configuration. In fact, what is really important about this is the fact that wsadmin can perform this configuration manipulation when the AppServer is stopped. To perform this manipulation, you must know how to use the AdminConfig scripting object, which this chapter covers in great detail.

AdminConfig Overview

Looking at the AdminConfig.help() text,1 you see that this object can be used to perform several operations on WebSphere Application Server configuration objects. These operations include the following:

1 This is explained further in Chapter 7, “Introduction to Admin Objects.”

• List

• Create

• Remove

• Display

• Modify

Warning

Modifying a WebSphere Application Server configuration using scripting can render it invalid.

Hopefully this warning encourages you to be careful when modifying a configuration. Additionally, if changes are made to a managed node configuration while in “local mode,”2 a subsequent node synchronization request can cause any such changes to be discarded. Before making changes to your configuration, you should familiarize yourself with the backupConfig command script and make sure to save your configuration. Visit the WebSphere Application Server Information Center for documentation on the backupConfig and restoreConfig utilities.

2 Starting wsadmin with –conntype none as one of the command-line options forces it to be in local mode.

There are some challenges to learning how to use the WebSphere administration scripting objects within wsadmin, the greatest of which is the confusion about which administrative objects deal with which kinds of items. Some objects require a configuration identifier (config ID), while others require an MBean identifier. How, then, do you know which is to be used and when?

Configuration Identifier (config ID)

The simple answer is that administration objects that deal with configuration information require a config ID, whereas objects that deal with active application server resources require an MBean identifier. Let’s take a quick look at an example config ID, as shown in Listing 8.1.3

3 Unfortunately, config IDs can be so long that they won’t fit on a single line unless you use a very small font. The config ID shown should, in fact, be a contiguous string containing no end-of-line characters.

Listing 8.1 Example config ID

image

Figure 8.1 shows a slightly different perspective of the information specified in this config ID. This listing shows the directory structure under the WebSphere Application Server installation directory for which this config ID applies (that is, .../profiles/AppSrv00/config). Notice how we can use each portion of the config ID, in Listing 8.1, to traverse the directory structure shown in Figure 8.1 to the exact location of the applicable configuration file (server.xml). Each portion of the config ID from Listing 8.1 is underlined in Figure 8.1 to more easily locate and identify each element.

Figure 8.1 Directory structure view of config ID

image

What about that “stuff” after the filename, specifically the Server_1219933843508? If you look at the contents of the specified file, you find that this information is contained within the file as an attribute of the process:Server tag (that is, xmi:id="Server_1219933843508"). So not only does the config ID point directly to a specific file, but it also identifies a unique element within that XML configuration file.

The fact that you can use a config ID to locate a specific file and XML tag within the file should not be taken as encouragement to do so. Please let wsadmin and the scripting objects do this for you. Manual manipulation of an XML configuration file could easily invalidate the configuration.

Containment Path

Another frequently asked question is, “How do I get the config ID for a particular configuration object?” Well, there a few different ways to do it. Choose one technique over the other based on your personal preferences and the information you have available.

In the documentation, the most common technique uses the AdminConfig.getid() method to obtain a config ID. However, this call requires that a containment path be provided. A containment path is a collection of one or more name/value pairs in a specific order. The name portion is required and is a configuration object type and is followed by a colon and an optional value. The name/value pairs are delimited using the slash character '/'. Some example containment paths would be '/Cell:/', or '/Cell:myCellName/'. Each containment path, when passed to the AdminConfig.getid() method, result in zero or more config IDs. Listing 8.2 shows some example calls to AdminConfig.getid() using a containment path (lines 3 and 5).

Listing 8.2 Example Use of AdminConfig.getid()

image

Should multiple name/value pairs exist within the containment path, the sequence of the name/value pairs must match the hierarchy of the types in the WebSphere environment. This means that the first type specified should contain objects of the second type specified, and so on. For example, a containment path in which the first type specified was Cell and the second type specified was Node would be valid because a cell is composed of node objects. However, a containment path where the first type is Node and the second type is Cell would not be valid because the hierarchical relationship does not match that of the WebSphere objects. Listing 8.2 (lines 7–8) shows that using an invalid containment path results in no match being found in the configuration. Additional information about this configuration object relationship is covered in the “Create and Modify Methods” section later in the chapter.

Configuration Types

What configuration object types exist? A call to AdminConfig.types() can be used to get a list of all valid types. A small, stand-alone script (AdminConfigTypes.py) is provided that displays these types, identifies the number of types, and gives the length of the shortest and longest.

When you execute the script on a version 6.1 installation, almost 600 supported types exist. For a version 7.0 installation, it was closer to 800. This means you can’t possibly cover these types in any amount of detail. What you can try to do is to present some techniques for investigating types and the object methods that use them. Hopefully these techniques will assist you in your investigations about those types in which you are interested that are not discussed within this book.

In addition to the getid() method, the AdminConfig.list() method can also be used to find one or more config IDs. For example, the config ID shown in Listing 8.1 happens to be for an unmanaged application server. The technique used to obtain this server config ID depends upon your environment. For an unmanaged application server, it can be as simple as using something like print AdminConfig.list('Server')).

What’s the difference between using the getid() and list() methods to obtain a config ID, and why might you prefer one method over the other? The primary difference is that when the AdminConfig.list() method is used, a type is specified (for example, 'Server') instead of a containment path. The result of this call can therefore include multiple objects.4 By using the getid() method and providing qualified name/value pairs, the result might already be “filtered.”5 In this case, additional processing to identify or select one of the results might not be necessary. For example, Listing 8.3 shows how an unqualified containment path (lines 5–8) is the same as using the call to AdminConfig.list() and specifying the same type (lines 1–4). However, by qualifying the containment path (lines 9–10), no additional code is needed to get to the specific config ID in question (that is, server1).

4 For example, the result of calling AdminConfig.list('Server').splitline() in a clustered environment should result in a list containing all of the config IDs of all of the application server in the cell.

5 The result of calling AdminConfig.getid('/Cell:cellName/Node:nodeName/').splitlines() contains only config IDs of items within the specified containment path, not the entire cell.

Listing 8.3 AdminConfig.list() and getid() Method Examples

image

Because both the list() and the getid() method can result in multiple values, it is best to have your scripts takes this into account. To do so, use the string splitlines() method to convert the string into a list of strings, with each element being the config ID of an application server. The splitlines() routine uses end-of-line markers as line delimiters.6 Should the result of the AdminConfig.list() call be a single entry (for example, a single application server), the result of the splitlines() call will be a single element list.

6 Please note that the string splitlines() method that exists in the version of Jython in WebSphere Application Server 6.0 doesn’t work as expected, so you should use split(' ') instead.

Using a config ID

What can be done once you have a config ID? One of the most useful AdminConfig methods is AdminConfig.show(), which can be use used to provide information about the specific attributes and values that exist for the resource identified by the specified config ID. As seen in Listing 8.4 (lines 3–14), you can see that the result is many lines of text, with each line containing something that looks like a name/value pair. Interestingly enough, each line starts and ends with square brackets ('[]'). One way to use this information would be to process, or parse, each line, searching for entries of interest. However, there is an easier and better way.

Listing 8.4 AdminConfig.show() Example

image

image

Using the understanding of how the text result of the show() method is formatted, it is a simple matter to create a routine to call the show() method for a given config ID and convert the result into a dictionary, which can then be returned. A routine to do just this was created and is part of a collection of utility routines provided in a module name WAuJ_utilities. Listing 8.5 shows an example use of this routine to demonstrate its usefulness, and Table 8.1 explains this example in detail.

Listing 8.5 Example Use of showAsDict Function

image

Table 8.1 showAsDict Example Explained

image

The usefulness of a routine like showAsDict() should be readily apparent, especially to anyone who has tried writing code to extract a particular piece of information out of the text returned by an AdminConfig or any of the other object methods. Being able to write something simple like dict['name'] to access the name attribute of a given object is very powerful (and intuitive).

Show and Tell Methods

Other AdminConfig methods exist that can also be used to learn more about specific types with which you are working. For example, the attributes() method can be used to “show the attributes for a given type,” where the show() method will “Show the attributes of a given configuration object.” We have already discussed and seen the results of using the AdminConfig.show() method. The parameter for the AdminConfig.attributes() method is a configuration type, so you use print AdminConfig.attributes('Server') to display the attributes that are available for application servers.

Listing 8.6 contains a more complicated interactive session that demonstrates one way to process the results of the AdminConfig.show(), and attributes() methods in order identify attributes that don’t exist in the output of show() for a particular application server. Table 8.2 explains the interactive session in detail. Note how the showAttribute() method is used to display the attribute information for a given configuration object.

Listing 8.6 Missing Attributes with No Default Value

image

Table 8.2 Explanation of Missing Attributes with No Default Value

image

Interestingly enough, some of the entries listed from the attributes() method call don’t appear from the earlier call to the show() method. If you look again at the methods listed in the help text, you see that another method called defaults() exists. Printing the help for this method shows what appears to be an overlap of function between the two methods (attributes and defaults). However, if you print the output from each method in which you specify the type parameter as 'Server', the descriptions are significantly different. The output of the two routines might also raise some questions.

If an attribute doesn’t have a configured value, then you would expect that attribute to have a default value. Why do we find attributes for which no default value is provided? And there is yet another AdminConfig method called required(). If you display the required attributes for type 'Server', using a statement like print AdminConfig.required('Server'), you see that only the name attribute is required. How is it that so few are required as well as so few default attributes? Does the AdminConfig.showAttribute() method return a value for the undefined attributes that don’t have a default value? Unfortunately, these are questions only the developers could answer.

The last of the “show and tell” methods, as we like to think of them, is the AdminConfig.showall() method. The difference between the show() and showall() methods is that the show() method returns the attributes of the specified configuration object, and the showall() method does that as well as recursively including all of attributes of the referenced configuration objects. For example, given a configuration object for an unmanaged application server (e.g., as seen in Listing 8.4) using AdminConfig.show() results in 12 attributes. On the other hand, using AdminConfig.showall() results in almost than 650 attributes and nested attributes.

The unfortunate thing about the results of using the showall() method is that the result is difficult to analyze at least programmatically. Is there anything that we can do to traverse a configuration object hierarchy searching for a specific attribute? Well, nothing is provided, so you have to develop your own.

How might you go about doing this? Let’s begin by taking a look at the information that exists in the output from calling the AdminConfig.show() method using some config ID. Because the showAsDict() routine does this and returns a dictionary, it should be easy to realize that each line of the result should contain a name/value pair. What kind values are returned? It depends on the kind of configuration object that was passed to the show() method.

Listing 8.4, shown earlier, details possible values such as the following:

• Empty lists (that is, open and closed square brackets [])

• Names, or identifiers (for example, true, false, server1, APPLICATION_SERVER)

• Numeric values (such as port numbers, threshold values, and timeouts)

• Single config ID (with or without the leading name)

• Config ID lists (contained within [...])

The ones of special interest at this time are the ones containing either a single config ID or a config ID list. What you need to be able to do in order to traverse the configuration object hierarchy is to be able to recognize, extract, and use these config IDs in order to process the referenced configuration object.

The best way to identify a single config ID is by using a regular expression (RegExp). How do you build a RegExp to identify a config ID? Very, very carefully. The important part is to realize that they begin and end with parentheses (with an optional identifier prefix). What would this kind of RegExp look like? Simply stated, the expression is '^(w*([^)]+))$'. Table 8.3 explains this RegExp in detail.

Table 8.3 config ID RegExp Explained

image

A similar RegExp for config ID lists is '^"?[([^]]+)]"?$'. It differs from the previous RegExp in the following aspects:

• Optional surrounding double quotation marks

• Surrounding square brackets ([])

• Space between each config ID

The good news is that these work great for the vast majority of config identifiers. The bad news is that in testing, we found something that we did not expect. It is valid to have a config ID that has a name (the portion before the opening parenthesis) containing blanks. If a blank exists in the name, the whole config ID is surrounded by double quotes. This means that the simple RegExp pattern (for example, the 'w*') isn’t valid for all config IDs. So the new and improved RegExp to match this more complete understanding of config IDs would be '(w*([^)]+)|"[ w]+([^)]+)")'. The first part of this RegExp pattern (the portion before the '|') is the same. The second part allows double quotes as well as spaces within the name. The revised code is available in findAllAttributes.py, and includes code that demonstrates how the function can be used.

The “test” code that is included within findAllAttributes.py script (that is, the code executed when the file is executed instead of being imported) tries to find attributes having the name “port.” Interestingly enough, this code doesn’t provide information about all of the ports that are used by an application server. Unfortunately, this code is quite a bit harder to understand and follow. Additionally, it is not clear how useful or applicable that it might be without additional experimentation and testing.

A better and more useful example would be a script that could be used to find and display the port numbers configured for an application server. Interestingly enough, this script is very easy to create and, in fact, has been provided in the script named ListPorts.py.7 The sample output of this script is shown in Listing 8.7. To perform this task, the script only needs to:

7 It is not as easy as using an existing method like AdminTask.reportConfiguredPorts(), but we haven’t gotten to the AdminTask scripting object yet. Additionally, the output of ListPorts.py looks better.

1. Obtain the list of ServerEntry config IDs using:

SEs = AdminConfig.list( 'ServerEntry' ).splitlines()

2. For each ServerEntry config ID, get the NamedEndPoint config IDs using:

NEPs = AdminConfig.list( 'NamedEndPoint', SE ).splitlines()

3. For each NamedEndPoint, use the endPoint attribute to get the port number:

image

Listing 8.7 Sample Output of ListPorts.py

image

This shows how challenging it can be to access a particular kind of information from the configuration. It requires quite a bit of knowledge (such as the configuration type names of the objects), as well as some perseverance to get the information you want displayed in a specific format. However, it also shows how easy and intuitive it can be to use a utility routine like showAsDict() to simplify your programs at the same time they are being made easier to read and understand.

Create and Modify Methods

Another category of AdminConfig methods is associated with the creation and modification of objects. Let’s begin by taking a look at what is required to create a configuration object like an application server. Looking at the help for the create() method,8 you find that the Type, the Parent, and Attribute parameters are required.

8 That is, print AdminConfig.help('create').

The Type parameter is pretty straightforward because it is used to identify the kind of object to be created. But what does the Parent parameter represent? To understand that, it is important to realize that you can’t create stand-alone configuration objects. Every object has a place in the configuration hierarchy. So the Parent parameter is the config ID of an existing object that is allowed to “contain” the kind of object being created. This means that each object type can only occur in specific places in the object hierarchy.

Is there a way to find out the kinds of objects that are allowed to be parents for a specific type? Yes, there is. To do this, you can use the AdminConfig.parents() method and pass it the configuration type you want to create. For example, executing print AdminConfig.parents('Server').splitlines() results in ['Node', 'ServerCluster'] being displayed. This means that to create a server object, you must provide the config ID of either a Node object, or a ServerCluster object.

Can you use the AdminConfig.parents() method and the AdminConfig.types() method to determine all of the allowed parent/child object relationships? Sure, to do so, you first need to find out what is returned by the AdminConfig.parents() method when a type is specified for which a parent type does not exist. Once that is done, you can build a simple script to generate the list of configuration type names. For each type name, you can also determine the valid parent types that can contain objects of the current type.

Our first attempt at writing a script to use this technique can be found in parents.00.py. It includes no “unnecessary” code and simply displays the configuration types for which the AdminConfig.parents() method returns a “valid” result. This is kind of alright, but the output is hard to read. With a little additional code (as found in parents.01.py), the information can be displayed in an easier to read format. The main thing wrong with this output though, is the fact that the information is displayed from the perspective of the different configuration types. For each type, you can see the valid parent types listed.

How can you turn this around? Instead of listing the valid parent types for each type, what would it take to list the valid child types for parent type? That’s what happens with the parentTypes.py script. This provides an easier to understand representation of the hierarchical relationships. A portion of the output from this script is shown in Listing 8.8. The types on the left are allowed (by the WebSphere Application Server product) to be parent types. For each of these valid parent types, the right column shows the allowed “child” types.

Listing 8.8 Example ParentTypes.py Output

image

Now that you understand what the Parent parameter represents, you can use the following steps to create a new configuration object:

1. Decide the type of object to be created.

2. Locate the type name using the AdminConfig.types() method.

3. Verify that an object of this type can be created using AdminConfig.parents().

4. Identify the required, default, and allowed attributes for this type using the AdminConfig.required(), AdminConfig.defaults(), and AdminConfig.attributes() methods.

5. Identify the appropriate Parent type of the new object.

6. Locate the config ID of the object under which the new object is to be created.

7. Determine the necessary attribute values that need to be provided to the create call.

8. Call the AdminConfig.create() method to create the desired object using the appropriate parameters.

For something like a Server (which only requires that the name attribute be specified), this could be as simple as the following:

image

Wow, that’s pretty simple. Is that all there is? Almost, but not quite. One of the things that wsadmin does is to provides a level of protection. When any configuration change is made, this change is not actually made to the existing configuration. These changes are kept in a workspace separate from the permanent configuration. All changes to the configuration are stored in this workspace until the changes are committed (using the AdminConfig.save() method) or discarded, either by terminating wsadmin (using either the quit, or exit command9) or by calling the AdminConfig.reset() method.

9 Should modification be made when either the quit or exit command are issued, a warning message will be displayed to remind you to save your changes should you desire to do so.

What if you change your mind about the object you created? If that was the only change made, you could simply discard all of the changes. However, if you are uncertain about all of the changes, you can first display the list of modified files (using the print AdminConfig.queryChanges() statement), or you can obtain the config ID of the created object and then use the AdminConfig.remove() method to remove this object from the configuration. Remember, though, that should you invoke the remove() method, the specified object is not actually removed from a saved configuration until the AdminConfig.save() method is called.

A call to the AdminConfig.hasChanges() method can be used to determine whether or not changes have been made to the configuration. The result of this call is either 0, which indicates that no changes have been made, or 1, which indicates that unsaved changes exist in the workspace.

The last method in this category is the AdminConfig.modify() method. As you might expect, it can be used to make changes to the attributes of a particular configuration object. To do so, a config ID is required, as well as the list of attributes to be modified. A trivial example to demonstrate this would be the following:

image

From this, you would see that the name attribute of the created server object was modified by the call to the AdminConfig.modify() method.

Configuration Verification/Validation

This chapter started with a warning about the possibility of corrupting a configuration using scripting. A number of questions come to mind related to what you can do to protect your WebSphere configuration from corruption. Is there a way to check for a corrupt or invalid configuration? What can be done to minimize the possibility of configuration corruption? What happens if conflicts exist in the configuration?

The wsadmin utility is configured, by default, to use the highest level of protection and verification available. As was discussed in Chapter 6, in the section “The wsadmin Environment Initialization Phase,” many of these default settings are configurable in the wsadmin.properties file that is loaded when wsadmin starts. The settings provided in the properties file that are related to validation/verification are as follows:

validationOutput

validationLevel

crossDocumentValidationEnabled

Each of these attributes has a prefix of com.ibm.ws.scripting, and each has a comment block in the properties file identifying the role provided by each attribute, as well as the default value, should one exist.

If your script needs to query or change either of the last two attribute values, the AdminConfig object has getter and setter methods to do so. Specifically, you would use getValidationLevel(), setValidationLevel(), getCrossDocumentValidationEnabled(), and the setCrossDocumentValidationEnabled() methods.

Interestingly enough, both the getter and setter for each of these values returns a string containing the requested setting. In each case, the last blank delimited word in the result is either the current value (in the case of the getter method) or the value after making the requested change (in the case of the setter method). So, in order to save, or access this value, you can use something like val=AdminConfig.setValidationlevel('high').split(' ')[-1].

Another related setting exists for which only getter and setter methods are available (i.e., no corresponding attribute is available within the wsadmin.properties file). The possible saveMode values are 'rollbackOnConflict' (the default) and 'overwriteOnConflict'. This value defines the action to be performed should a configuration conflict occur during a request to save the configuration.

So some amount of checking is performed during a configuration save request. Additionally, a call to the AdminConfig.validate() method can be used to perform a configuration validation. The result of this call will be a string identifying the number of validation messages generated (and written to the specified file) based on the current workspace contents, the value of the cross-document validation-enabled flag, and the validation level setting.

Please note that with the highest level of validation enabled (which is the default setting), calling the AdminConfig.validate() method can result in messages being written to the validation output file even before any change to the configuration has been made.

The result of calling the AdminConfig.validate() method is a string that includes the name of the validation output file and the number of each severity message written to the output file.

Should you be interested in determining the number of messages of each severity that were written to the output file without parsing the validate() result string, another method exists. Calling AdminConfig.getValidationSeverityResult() and passing a numeric (severity) value returns a number representing the number of messages of the specified severity that were written to the validation output file.

Document Manipulation Methods

Another category of methods exists that should be used with caution. These methods are related to the direct manipulation of files, or documents in a specific portion of the application server configuration hierarchy. The methods are checkin(), createDocument(), deleteDocument(), existsDocument(), and extract().

An example script has been provided (documents.py) that shows one way in which these methods might be used. Please note the restriction that exists that limits the location(s) of these “documents” within the application server hierarchy.

Warning

Just because you can use these methods to copy WebSphere Application Server configuration (i.e., XML) files out of the directory structure and then edit and replace them, this should not be taken as encouragement to do so. It is extremely important that WebSphere configuration files be managed by the administrative objects.

Miscellaneous Methods

The remainder of the AdminConfig methods are grouped together simply as a matter of convenience. The first of these methods are related to clusters:

convertToCluster()—Used to convert an existing server configuration object to ServerCluster configuration object.

createClusterMember()—Used to create a new ServerCluster configuration object in an existing cluster and on an existing node.

The former is used in a script of the same name (convertToCluster.py) that is available with the other scripts. Interestingly enough, in order to create a fairly complete script that performs a reasonable amount of error checking and verification doesn’t require that many lines of code. At first glance, convertToCluster.py appears to have a little over 300 lines of code, but this is slightly misleading. There are lots of comment and empty (or blank) lines. When these are removed, it seems that approximately 75–80 lines of code were needed in order to provide a complete script that makes use of this AdminConfig method for converting an existing application server into a cluster member. We’ve found that quite powerful.

The latter is also demonstrated in a script of the same name as the AdminConfig method. It is a bit more complicated than the previous example. It also requires less than 200 lines of executable code and less than 350 total lines to have a reasonable script that includes command line parameter processing, script usage (documentation) information, error checking, exception handling, and numerous comments to explain how the script functions.

Two of the AdminConfig miscellaneous methods are related to Templates. The first of these, listTemplates(), is used in the createClusterMember.py example script to obtain a list of config IDs for templates of a given type. As it is used in that script, an optional parameter string is provided to identify text that must exist in each returned config ID ('APPLICATION_SERVER'). The other Template-related method is createUsingTemplate(), which can be used to create an object of a specified type in the configuration hierarchy. Another script is provided, CreateServer.py, that shows one way to use the createUsingTemplate() method.

The last of the paired miscellaneous AdminConfig methods are related to ResourceAdapters:

installResourceAdapter()—Used to install a J2C resource adapter with the given .rar filename and an option string in the node.

uninstallResourceAdapter()—Used to uninstall a J2C resource adapter with the given resource adapter config ID.

These methods are fairly well-documented in the online documentation. Unfortunately, we haven’t had an opportunity to create scripts that use these methods. If you have need of scripts that make use of these methods, you might want to try modifying one of the available scripts to make use of these methods.

The final miscellaneous method is getObjectName(). It can be used to lookup and return the available MBean identifier for the specified object. For example, given the config ID of an application server, this method can be used to obtain the MBean identifier for an active instance of this application server. Remember, though, if the corresponding configuration object is not active, or if wsadmin is in local mode (that is, if wsadmin was started with the -conntype none command-line parameter), then either an exception will occur, or the result of the method call will be an empty string.

Summary

This chapter described the AdminConfig administrative scripting object, which deals with viewing and manipulating items in an application server configuration. We saw how objects can only be created when the config ID of an existing object of the appropriate type is specified. We also saw how some of the utility routines provided with this book can be used to make configuration viewing and manipulation easier.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.122.66