CHAPTER 6

image

Advanced Scripting

Much of the power of a command-line interface like EM CLI is the ability to automate the execution of multiple commands at a time. This chapter dives deep into the marriage of Python and EM CLI in what is known as interactive and scripting mode.

The first part of the chapter reviews some of the history of where Python, Jython, and JSON came from. Being able to take advantage of Python functionality in EM CLI requires a basic understanding of programming and Python syntax. An introduction to Python in this chapter, including examples, will allow even the first-time Python user to tackle Python efficiently in EM CLI.

Finally, a detailed example will feature the use of a Python class object to take advantage of EM CLI functionality to modify the properties of multiple targets at a time. This is a common task that is tedious in the graphical user interface and is necessary to understand and use in EM CLI.

History of Python

Python was created in the late 1980s by Guido van Rossum and originated from an interpreted language called ABC. At the time, van Rossum was working with ABC and liked the syntax but wanted to change some of the functionality. During the Christmas holidays of 1989, van Rossum designed the language that would later be called Python. Python was officially released in 1991 while van Rossum worked for a company called Stichting Mathematisch Centrum in Amsterdam.

Python did not get its name from the dangerous reptile, but rather from the popular British BBC comedy series, Monty Python’s Flying Circus. Van Rossum was a fan of the show and needed a name that was fitting for the language.

Van Rossum continues to have a central role and has been named Python’s Benevolent Dictator for Life (BDFL). This term was coined for van Rossum but has become a common description for open-source software that has retained its creator as the final decision maker for disputes or arguments within the community.

Python is a programming language, but because the code is compiled at runtime, it can also be considered a scripting language. There is not always a clear delineation between the two, and the definition of each has changed over time. Generally, programming languages must be compiled before they can be run and scripts are run from a set of commands, either interactively or from a file. Since Python can do either and both at the same time, the answer to the question “Is Python a programming or a scripting language?” is “Yes!”

Jython

Jython is an implementation of Python written in Java. Jython was initially created in late 1997 to replace the “C” implementation of Python with Java for performance-intensive code accessed by Python programs. Knowing how to code in Java, while beneficial to working with Jython, is not a prerequisite. In fact, one can use Jython with no knowledge of Java whatsoever. However, when using Jython it is likely one will come across Java code, as Jython programs use Java classes in addition to Python modules.

The Enterprise Manager Command-Line Interface is written almost entirely in Java and Jython. The download of EM CLI from the OMS is a JAR file that requires the Java executable to install. When EM CLI is used in interactive mode, the command-line interface is Jython.

The rest of this chapter will refer to Python and Jython interchangeably. This is because most users start out with Python and subsequently learn Jython, or use only Python language (no Java) when using Jython. There is a great deal of information and tutorials for Python on the Internet and in books, so it is advised to search “Python” instead of “Jython” when looking up syntax, examples, and so forth.

If you’ve ever used any of Oracle’s middleware products, you have likely already come across Jython. WebLogic Application Server's Scripting Tool (also known as “wlst”) also uses Jython.

JSON

The output of EM CLI commands will be displayed in one of two ways. The standard tabular or columnar format is known as “text” mode. The text mode is generally the most readable form of the data up to a certain point. For example, a table with five columns displays well in text mode, but a table made up of 20 columns is very difficult to read or parse. Text mode is the default mode of display for EM CLI interactive mode.

An alternative to text mode for displaying command output is JavaScript Object Notation, commonly known as JSON. JSON was originally derived from JavaScript and has since become a universal tool for data interchange. Nearly every programming and scripting language not only uses and understands JSON, but also has a vast array of built-in functionality for it.

JSON is the default method of storing and displaying command output for the “scripting” mode of EM CLI, which was built for processing EM CLI commands and output. It is expected that the data received from the commands processed in an EM CLI script will be manipulated in some way.

Getting Started

Python was built for ease of use and brevity. For those reasons, it is not difficult for a novice to learn the basic functionality of the language and to use it to accomplish tasks. DBAs often have an advantage when it comes to learning Python because of their experience with PL/SQL, Java, shell scripting, various command-line interfaces, and analytical tendencies.

Perhaps Python’s most noticeable feature is the use of indentation for block structure. Instead of using open and close statements to enclose a block of flow control, such as if statements, Python uses four spaces to indent lines of code that belong to a block. Therefore, the line above the first line not indented ends the block. The first line of the block is always followed by a colon.

>>> for VAR in myLoop:
...     do this command
>>> this line does not belong in the loop

Both Python and Jython (as well as EM CLI and WLST) have an interactive interface in which any command executed in a script can be written or copied and executed to see the results in real time. Most Linux hosts will have Python installed. To invoke the interactive interface, simply type python:

[root@server ~]# which python
/usr/bin/python
[root@server ~]# python
Python 2.6.6 (r266:84292, Jul 10 2013, 06:42:56)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>print("this is the interactive Python prompt")
this is the interactive Python prompt
>>>exit()

Hello World!

“Hello World!” is the most common introduction to any programming or scripting language. The following example uses three lines of code to demonstrate five important concepts in Python that will be used in nearly every script. Notice the lack of extraneous text or commands in each line.

>>> myvar = 'Hello World!'
>>> if myvar:
...     print(myvar)
...
Hello World!

The first line shows how to assign a value to a variable. The variable does not need to be created before being assigned a variable. Both steps are done at once. The value assigned to myvar is a string value, making the variable a string variable.

The second line starts an if block. The block says, “If the variable myvar exists and has a value assigned to it, proceed to the next line in this block.” The opening line of code blocks always ends with a colon.

The third line prints the value of the myvar variable, followed by a new line. The print function always includes a newline character at the end of the printed string.

The fourth line requires a carriage return to indicate the code block is finished. This is not the case in a script, where blank lines after code blocks are not necessary.

The fifth line shows the output of the code-block commands. This would indicate that the myvar variable did indeed exist and had a value assigned to it, so the line within the code block was executed, evidenced by the success of the print function.

Finding Help

There is myriad information available on the Internet regarding Python. A simple Internet search will return more than enough results to refresh one’s memory about a forgotten command. But, Python does one better. A full suite of help is available right from the command line.

Basic syntax on how to use the internal help in Python can be read by calling the help() function, which puts the user into another command-line interface within Python where almost any keyword (including “help”) found anywhere in Python can be entered to provide easy-to-understand information:

>>> help()

Welcome to Python 2.6!  This is the online help utility.

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics".  Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".

help>

For example, if I need to know something about a string, I can type string within the help> command line, and then I can read about the many ways strings are used in Python. Perhaps a simpler way to find information is to look up a topic by typing topics. The command prints out a list of available topics, including STRINGS. Typing STRINGS presents an easy-to-understand article about strings in Python. Typing the letter q will exit the article, and typing <CTRL-D> will exit the help command line:

help> topics

Here is a list of available topics.  Enter any topic name to get more help.

ASSERTION           DEBUGGING           LITERALS            SEQUENCEMETHODS2
ASSIGNMENT          DELETION            LOOPING             SEQUENCES
ATTRIBUTEMETHODS    DICTIONARIES        MAPPINGMETHODS      SHIFTING
ATTRIBUTES          DICTIONARYLITERALS  MAPPINGS            SLICINGS
AUGMENTEDASSIGNMENT DYNAMICFEATURES     METHODS             SPECIALATTRIBUTES
BACKQUOTES          ELLIPSIS            MODULES             SPECIALIDENTIFIERS
BASICMETHODS        EXCEPTIONS          NAMESPACES          SPECIALMETHODS
BINARY              EXECUTION           NONE                STRINGMETHODS
BITWISE             EXPRESSIONS         NUMBERMETHODS       STRINGS
BOOLEAN             FILES               NUMBERS             SUBSCRIPTS
CALLABLEMETHODS     FLOAT               OBJECTS             TRACEBACKS
CALLS               FORMATTING          OPERATORS           TRUTHVALUE
CLASSES             FRAMEOBJECTS        PACKAGES            TUPLELITERALS
CODEOBJECTS         FRAMES              POWER               TUPLES
COERCIONS           FUNCTIONS           PRECEDENCE          TYPEOBJECTS
COMPARISON          IDENTIFIERS         PRINTING            TYPES
COMPLEX             IMPORTING           PRIVATENAMES        UNARY
CONDITIONAL         INTEGER             RETURNING           UNICODE
CONTEXTMANAGERS     LISTLITERALS        SCOPING
CONVERSIONS         LISTS               SEQUENCEMETHODS1

help> STRINGS
String literals
***************

String literals are described by the following lexical definitions:

   stringliteral   ::= [stringprefix](shortstring | longstring)
   stringprefix    ::= "r" | "u" | "ur" | "R" | "U" | "UR" | "Ur" | "uR"
   shortstring     ::= "'" shortstringitem* "'" | '"' shortstringitem* '"'
...

:q

help> <CTRL-D>
You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)". Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
>>>

The help function is also very useful in EM CLI. The developers spent a lot of time making sure information is contained within the program. Unlike Python, typing help() does not drop into another command line; it simply prints out a list of verbs, sorted and grouped by category.

The individual verbs can then be specified in the help function for more detailed information on that verb. For example, typing help('list_active_sessions') in EM CLI would print out detailed information on the list_active_sessions() function.

Python Objects

An object in Python is an entity that can hold data and, in most cases, be manipulated by changing the data contained within the object or by changing the attributes of the object itself. The content and complexity of a Python object depends on the object type. For example, if you need an object with just a single digit, you would use a number object. If you need a multiple key/value object, you would use a dictionary object. The life of an object begins when it is created or assigned a value and ends when it is explicitly destroyed or when the Python session ends. This section lists the most common object types and shows examples of how to create and use them.

Numbers and Strings

A number object is an entity that contains a single integer value. A string object is an entity that contains a single string value. These types of objects are mutable, which means the assigned value can be changed or replaced at any point during the life of the object. These are by far the most commonly used objects in Python.

The “Hello World!” example introduced the most basic object in Python: the string. Strings are used everywhere in Python, and most other objects are made up of numbers and strings. Strings can include numbers, but numbers cannot include strings.

This example shows how to create a simple string object by assigning a string to a variable:

>>> mystring = 'Here is my string'
>>> type(mystring)
<type 'str'>

I can create a number object by assigning a number to a variable. int stands for integer:

>>> mynumber = 12345
>>> type(mynumber)
<type 'int'>

Numbers and strings are different types of objects. Trying to join a string value to a number value results in error:

>>> mystring + mynumber
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

Lists

Lists are a common way to store an indexed group of values within an object. A simple list is assigned to an object in a similar way to how a string is assigned to an object, the difference being that the object is now a list object, not a string object. The significance of this is not as great in Python as in other languages, but it is a good idea to have an understanding that there is a difference that can potentially have an impact on the behavior of a program.

This example shows a list object with a single value. The values assigned to a list are encapsulated by brackets. Without the brackets this would be a simple string object. See here:

>>> mylist = ['one']
>>> print(mylist)
['one']
>>> type(mylist)
<type 'list'>
>>> mystring = 'one'
>>> type(mystring)
<type 'str'>

Simple Lists

A list with a single value can be useful, but the purpose of lists is to be able to assign multiple list values within a single object. These values are referred to as elements. In the previous example, a value of one was assigned as the first element in the mylist object.

This example creates a list called mylist with five members. Printing the list displays it exactly how it was created. The five elements are printed within an opening and closing bracket. The brackets are what tell us that this is a list and not a string or any other type of object. A list index of 2 is actually the third element in the list since the index numbers start at 0.

The power of lists comes from the plethora of ways that the individual elements can be manipulated. For example, any element can be queried from a list by specifying its index number. The index of a list starts at zero, so the first element of a list is assigned the number zero:

>>> mylist = ['one', 'two', 'three', 'four', 'five']
>>> print(mylist)
['one', 'two', 'three', 'four', 'five']
>>> type(mylist)
<type 'list'>
>>> print(mylist[2])
three

A list is mutable, which means it can be manipulated after it has been created. It can be lengthened or shortened, and the elements can be changed. This example shows the mylist object being appended with the value of 6. The number of elements in this object goes from five to six:

>>> mylist.append('six')
>>> print(mylist)
['one', 'two', 'three', 'four', 'five', 'six']
>>> print(len(mylist))
6

The len() function used in the previous example displays the number of elements in the list. Like many functions, len() can be used on lists, strings, and all other object types.

The next example shows how to further manipulate a list by extracting and deleting the last element of myshortlist. The first command in this example does three things. The pop function extracts and then deletes from mylist the last element, creates the myshortlist list object, and assigns to it the “popped” element. If the pop() function weren’t preceded by an object assignment, it would simply print the element:

>>> myshortlist = mylist.pop()
>>> print(myshortlist)
six
>>> print(mylist)
['one', 'two', 'three', 'four', 'five']

Lists in EM CLI

Lists play a critical role in EM CLI. The output of the functions in EM CLI may produce a list with hundreds or even thousands of elements, and those elements may be composed of strings, numbers, or other objects. The lists in EM CLI work in exactly the same way that they do in the previous examples.

Understanding everything about the following example is not important right now, but it is important to note that the list() function is one of the most-used functions in EM CLI and always returns a list object. This list has 29 elements that can be “popped” off one at a time and assigned to another object:

emcli>mytargs = list(resource='Targets').out()['data']
emcli>type(mytargs)
<type 'list'>
emcli>len(mytargs)
29
emcli>myshorttargs = mytargs.pop()
emcli>print(myshorttargs)
{'TYPE_DISPLAY_NAME': 'Oracle WebLogic Server', 'TYPE_QUA...

The output of the list EM CLI function is a list of target information.

Strings and Lists

This section will show how to accomplish the common task of finding a running Oracle database listener in Linux by using the Linux ps and grep commands. The first part of the example will show how to produce the process information. The second part of the example will show how to parse the output of the ps command using various Linux utilities, followed by the use of Python to accomplish the same task.

Listing 6-1 shows a detailed listing of an Oracle database listener background process. The ps -ef command lists all of the running processes on the server, which are piped into grep to limit the output to just the listener processes.

Listing 6-1. Find the Oracle database listener process in Linux

[oracle@server ]$ ps -ef | grep [t]nslsnr
oracle    1723     1  0 16:21 ?        00:00:01
/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit

The combination of ps and grep shows that a single listener is running and shows the full command in the eighth column of the output. The listener command actually includes two spaces, so to us it looks like the command spans the eighth, ninth, and tenth space-delimited columns. The awk command parses this information quite well, as shown in Listing 6-2.

Listing 6-2. Use awk to parse the process detail

[oracle@server ]$ ps -ef | grep [t]nslsnr | awk '{print $8,$9,$10}'
/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit

Listing 6-3 shows that Python would treat the output from the ps command as an object made up of pieces. The delimiter used in the previous example is one or more spaces. We can parse the same command using Python instead of awk.

Listing 6-3. Use Python to parse the process detail

>>> psout = 'oracle 1723  1  0 16:21 ? 00:00:01 
/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit'

>>> type(psout)
<type 'str'>
>>> psoutlist = psout.split()
>>> type(psoutlist)
<type 'list'>
>>> psoutlist[7:]
['/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr', 'LISTENER', '-inherit']
>>> ' '.join(psoutlist[7:])
'/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit'
>>> psoutstring = ' '.join(psoutlist[7:])
>>> psoutstring
'/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit'

psoutlist = psout.split() breaks apart the string into a list made up of string elements based on a delimiter of whitespace and assigns it to the psoutlist list object. psoutlist[7:] prints a subset of the psoutlist list object from the eighth (remember that a list index starts at 0) to the final element. psoutstring = ' '.join(psoutlist[7:]) joins the subset of elements back into a string delimited by a single space and assigns it to the psoutstring string object. What really makes Python shine is that this can all be done in one command, as shown in Listing 6-4.

Listing 6-4. Combine the commands from Listing 6-3 into a single command

>>> ' '.join('oracle 1723 1  0 16:21 ? 00:00:01 /u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit'.split()[7:])
'/u01/app/oracle/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit'

Listing 6-3 populates the psout variable directly with the output from the ps command. The process of capturing the output of the ps command could also be done directly from within Python, as shown in Listing 6-3a.

Listing 6-3a. Capture the ps command output using the Python subprocess module

>>> import subprocess
>>> mycommand = ['ps', '-ef']
>>> psoutput = subprocess.Popen(mycommand, stdout=subprocess.PIPE).communicate()[0].split(' ')
>>> for i in psoutput:
...     if 'tnslsnr' in i:
...         psout = i

Lists are a useful way of grouping strings and numbers together in Python, but sometimes a more complex object is required. Sometimes an index of single elements is not enough and we need a way of grouping keys and values together. This is especially true when working with EM CLI, where arrays are common and often complex. This is where Python dictionary objects come in.

Dictionaries

An equally important object type to understand in Python, and especially in EM CLI, is the “dictionary.” Similar to Java’s “hashtable,” Perl’s “hash” objects, and PL/SQL’s “associative arrays,” dictionaries are made up of a collection of key/value pairs. In other words, each element of the dictionary will be made up of one key and one value. The value can then be extracted by specifying its corresponding key. The syntax for calling a value is similar to that of calling a value in a list, but a key is specified instead of an index value. Listing 6-5 calls the value belonging to the key labeled 'second'.

Listing 6-5. Call a value from a dictionary by specifying the key to which it belongs

emcli>mydic = {'first': 'one', 'second': 'two', 'third': 'three', 'fourth': 'four', 'fifth': 'five'}
emcli>mydic['second']
'two'

Dictionaries use keys instead of a numerical index. JSON is represented as a dictionary object in Python since JSON is little more than an efficient key/value object. This can be shown clearly in EM CLI, as seen in Listing 6-6.

Listing 6-6. Use the list( ) function in EM CLI to show a JSON dictionary

emcli>set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')
emcli>list(resource='Targets').isJson()
True
emcli>type(list(resource='Targets').out())
<type 'dict'>
emcli>list(resource='Targets').out()
{'exceedsMaxRows': False, 'columnHeaders': ['TARGET_NAM...

Most EM CLI functions include an isJson function, which returns a boolean result indicating whether the result set will be JSON or not. This command indicates that the result set will return JSON instead of text.

By default, the interactive mode of EM CLI is text mode. The first command changes that behavior so that all result sets are returned as JSON.

The fourth line calls the out sub-function of the list function. This out function either prints to the screen or feeds to another object the output of the function that is calling it. In this case, the list function is returning information about the EM targets, and the type function is telling us that this output is being returned as a dictionary object.

The sixth line shows a very small part of the actual output, showing that it is contained within curly braces. The curly braces are what indicate that this object is a dictionary. Notice that one of the values is a list rather than a string. A dictionary value can be any other object type, including another dictionary.

Logon Script

It is necessary when using either interactive or scripting modes of EM CLI to establish a connection between EM CLI and the OMS. This is a repeatable procedure, so it is a good practice to have a script in place that can be called each time one calls a script or logs into EM CLI interactive mode.

The logon script is described below with comments regarding the various parts of it. The complete script is listed after the analysis.

The EM CLI classes and functions need to be imported first:

from emcli import *

This could also be done using a direct import:

import emcli

However, in this case each call to an EM CLI function would need to be qualified with the module name emcli.

The next step is to identify an OMS in which to connect:

set_client_property('EMCLI_OMS_URL', 'https://em12cr3.example.com:7802/em')

Following that one needs either to identify a valid SSL certificate file by which to authenticate with the OMS or to specify that you trust any certificate presented:

set_client_property('EMCLI_CERT_LOC', '/path/to/cert.file')

or

set_client_property('EMCLI_TRUSTALL', 'true')

Optionally, you can specify which format you would like the commands to return: “JSON” or “TEXT.” Unless there is a specific need for text output, JSON is the most versatile and easy to manipulate. However, it is also much more difficult to read:

set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')

or

set_client_property('EMCLI_OUTPUT_TYPE', 'TEXT')

Finally, specify with which user you would like to log in and, optionally, that user’s password. If the password is not specified, you will be prompted for it.:

print(login(username='sysman', password='foobar'))

The complete script is show in Listing 6-7.

Listing 6-7. start.py logon script

from emcli import *
set_client_property('EMCLI_OMS_URL', 'https://em12cr3.example.com:7802/em')
set_client_property('EMCLI_TRUSTALL', 'true')
set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')
print(login(username='sysman', password='foobar'))

Although EM CLI may be started from the same directory that contains the logon script, Jython will not see it unless the directory is within its search path. Listing 6-8 shows the error message you would receive if the start.py file you are trying to import isn’t in the Jython search path.

Listing 6-8. start.py not in the Jython search path

emcli>import start
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named start

The JYTHONPATH environment variable tells Jython the additional directories in which to search for modules to be imported. Set this variable before executing emcli. In Listing 6-9, the start module can now be successfully imported, either directly from the interactive mode command line or within a script in scripting mode.

Listing 6-9. Successful execution of the start.py script

[oracle@server ]$ export JYTHONPATH=/home/oracle/scripts
[oracle@server ]$ emcli
emcli>import start
Login successful

If you are not comfortable with including your password in the script (you shouldn’t be) and only specify the username parameter with the login function, you will be prompted for the password:

Enter password :  **********

Listing 6-10 shows an alternative method of authenticating that affords one the security of not including the password in the script while at the same time not requiring one to enter the password every time the script is invoked. The password in this example would be contained in the .secret file, which should have permissions set such that it can be read only by the operating system user invoking EM CLI.

Listing 6-10. start.py logon script using a password read from an external file

from emcli import *
set_client_property('EMCLI_OMS_URL', 'https://em12cr3.example.com:7802/em')
set_client_property('EMCLI_TRUSTALL', 'true')
set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')
f = open('/home/oracle/.secret','r')
pwd = f.read()
print(login(username='sysman', password=pwd))

The steps included in the start.py script need to be executed every time EM CLI is invoked in scripted or interactive mode, which is why it is important to script the login process. Any script called directly should invoke the import start command before doing anything else.

Python Scripting with EM CLI to Set Target Properties

EM CLI includes a function to change the properties of a target. If the properties of a small number of targets need to be updated, it is easy to simply execute the set_target_property_value() function for each of those targets. When the need arises to update the properties of tens or even hundreds of targets, executing the function for each target is not efficient. Fortunately, with EM CLI we can take advantage of the full scripting functionality of Python.

A for loop can be used to iterate through any object and perform any task on each one of those object pieces. For example, it is common to add or update target properties after a target has been added. Any task involving changing target properties for more than a couple of targets in the GUI is tedious and time-consuming. There is more than one way to accomplish this task within EM CLI, but all of them are easier and more efficient than the GUI can offer.

For demonstration purposes, we’ll use the interactive mode, but all of these commands will also work in scripting mode:

emcli>import start
Enter password :  **********
Login successful

Create an object containing all of the targets. Don’t worry about filtering out targets yet. That part will be done further down in the script:

emcli>myobj = get_targets().out()['data']
emcli>len(myobj)
29

In this case, I know I have 29 targets in my repository. We want to update the Lifecycle Status and Location properties of the targets we just added that have the prefix of TEST_ with Development and COLO, respectively. Since we may want to add to or change this list of properties in the future, we’ll put these into a dictionary as keys and values:

emcli>myprops = {'LifeCycle Status':'Development', 'Location':'COLO'}

We want to make sure we choose the correct targets for this update, so we will use regular expressions to filter the target names. Using regular expressions within Jython requires that we import another module:

emcli>import re

Now we can create the regular expression that we will use to filter the target names. This filter will be applied to each target name to determine if the target properties will be applied to that target:

emcli>filtermatch = '^TEST_'
emcli>myreg = re.compile(filtermatch)

Create a for loop to iterate through the targets. When writing programs like this, it is a good idea to write a little code and then test, especially when just getting started. Once the loop is created, we’ll run it with a simple print command to make sure we are iterating through the appropriate information.

Make sure the lines shown in the example preceded by three dots are indented properly. An indentation in Python is four spaces:

emcli>for targ in myobj:
...    print(targ['Target Name'])

This shows us that we are able to see all of the target names in our repository. Now we can apply the filter so we are only seeing the targets that start with TEST_:

emcli>for targ in myobj:
...    if myreg.search(targ['Target Name']):
...        print(targ['Target Name'] + 
...              '  -  ' + targ['Target Type'])

Now we are seeing just the targets we want to modify. Let’s add in the command to change the properties. However, instead of running the commands blindly, we’ll just print the command that the loop would have run to make sure the commands are what we want without actually affecting the targets:

emcli>for targ in myobj:
...    if myreg.search(targ['Target Name']):
...        mycommand = 'set_target_property_value(' + 
...                    'property_records="' + 
...                    targ['Target Name'] + ':' + 
...                    targ['Target Type'] + 
...                    ':LifeCycle Status:Development")'
...        print(mycommand)
set_target_property_value(property_records="TEST_em12cr3.example.com:host:LifeCycle
Status:Development")

set_target_property_value(property_records="TEST_em12cr3.example.com:3872:oracle_emd:LifeCycle
Status:Development")

The set_target_property_value() function commands are printed to the screen. These commands can then be copied and pasted within the same EM CLI session:

emcli>set_target_property_value(property_records="TEST_em12cr3.example.com:host:LifeCycle 
Status:Development")

Properties updated successfully

However, a problem pops up immediately if we try to run the command for an agent target:

emcli>set_target_property_value(property_records="TEST_em12cr3.example.com:3872:oracle_emd:LifeCycle 
Status:Development")

Syntax Error: Invalid value for parameter "INVALID_RECORD_ERR":
"em12cr3.example.com:3872:oracle_emd:LifeCycle Status:Development"

The target name TEST_em12cr3.example.com:3872 contains the default delimiter, the colon character, which means we need to include another parameter in the set_target_property_value() function to change the delimiter. The subseparator parameter will change the character that separates the different parts of the property_records parameter from a colon to an @ sign. Notice the addition of the mysubsep and myproprecs variables:

emcli>for targ in myobj:
...    if myreg.search(targ['Target Name']):
...        mysubsep = 'property_records=@'
...        myproprecs = targ['Target Name'] + 
...                     '@' + targ['Target Type'] + 
...                     '@LifeCycle Status@Development'
...        mycommand = 'set_target_property_value(' + 
...                    'subseparator="' + 
...                    mysubsep + '", property_records="' + 
...                    myproprecs + '")'
...        print(mycommand)
set_target_property_value(subseparator="property_records=@", property_records=" TEST_em12cr3.
                                        example.com@host@LifeCycle Status@Development")

set_target_property_value(subseparator="property_records=@", property_records=" TEST_em12cr3.
                                        example.com:3872@oracle_emd@LifeCycle Status@Development")

The commands are again printed to the screen. The properties for the TEST_em12cr3.example.com target have already been updated, so only the command to change the TEST_em12cr3.example.com:3872 target needs to be copied and pasted:

emcli>set_target_property_value(subseparator="property_records=@", property_records="TEST_em12cr3.example.com:3872@oracle_emd@LifeCycle Status@Development")
Properties updated successfully

The example we just looked at hardcodes the properties. This will work fine if the properties never change, but at the beginning of this exercise we created the myprops dictionary so we could easily change the property keys and values:

emcli>myprops = {'LifeCycle Status':'Development', 'Location':'COLO'}

Now let’s change the code we just created to take advantage of this variable. We’ll continue to use the debugging mode we included in the code to print the commands to screen and then add a nested for loop inside the existing for loop; we’ll then iterate through the items of the myprops dictionary. There is also an additional change here by adding the mydelim variable. By now you should be noticing a pattern; the more you parameterize, the easier your code is to read, change, troubleshoot, etc. Now if we decided to change the subseparator from an @ sign to something else, we would change it in one place, not four:

emcli>for targ in myobj:
...    if myreg.search(targ['Target Name']):
...        mydelim = '@'
...        mysubsep = 'property_records=' + mydelim
...        myproprecs = targ['Target Name'] + 
...                     mydelim + targ['Target Type'] + mydelim
...        for propkey, propvalue in myprops.items():
...            myproprecprops = propkey + mydelim + propvalue
...            mycommand = 'set_target_property_value(' + 
...                        'subseparator="' + 
...                        mysubsep + '", property_records="' + 
...                        myproprecs + 
...                        myproprecprops + '")'
...            print(mycommand)

These commands should work great for copy and paste, but we want to automate it further to skip the copy and paste and run the commands automatically in the code. However, when problems arise later, we don’t want to lose the ability to look at this verbose output again, so we’ll leave the debug in place and supplement it with the ability to choose whether we want to see the command printed to the screen or have it be run automatically by the script.

Adding a debug variable at the beginning of the script and checking for the value of that variable within the script is an easy way to enable or disable a “testing” mode. By default the debug variable will be false unless it is assigned a value, in which case it becomes true:

emcli>debug = ''
emcli>if debug:
...     print('True')
... else:
...     print('False')
...
False
emcli>debug = 'Yes'
emcli>if debug:
...     print('True')
... else:
...     print('False')
...
True

Now we can add this logic into the script and use it to decide whether we are debugging the script or executing it normally. There is also a line of output for normal execution mode that prints out some basic information about what the commands are doing:

emcli>debug = ''
emcli>for targ in myobj:
...     if myreg.search(targ['Target Name']):
...        mydelim = '@'
...        mysubsep = 'property_records=' + mydelim
...        myproprecs = targ['Target Name'] + mydelim + 
...                     targ['Target Type'] + mydelim
...        for propkey, propvalue in myprops.items():
...            myproprecprops = propkey + mydelim + propvalue
...            if debug:
...                mycommand = 'set_target_property_value(' + 
...                            'subseparator="' + mysubsep + 
...                            '", property_records="' + 
...                            myproprecs + 
...                            myproprecprops + '")'
...                print(mycommand)
...            else:
...                print('Target: ' + targ['Target Name'] + 
...                      ' (' + targ['Target Type'] + 
...                      ') Property: ' + propkey + 
...                      ' Value:    ' + propvalue)
...                set_target_property_value(
...                subseparator=mysubsep,
...                property_records=myproprecs + myproprecprops)
...
Target: TEST_em12cr3.example.com (host)
        Property: Location
        Value:    COLO
Properties updated successfully

Target: TEST_em12cr3.example.com (host)
        Property: LifeCycle Status
        Value:    Development
Properties updated successfully

This function makes things pretty easy, and we can easily change the properties or the target list filter. Using the debug variable, we can print out the commands instead of executing them. However, we can further simplify the process of updating target properties by moving all of these commands into a class. In addition, we’ll add more functionality in order to make it more robust and less prone to errors.

Python Class with EM CLI to Set Target Properties

Creating a Python “class” allows us to “package” code into a single unit in which all of the code that is part of the class is both aware of and can use all of the other code within that class. From a class, one can create an “instance.” An instance is an object in Python, and all of the changes to pieces of that instance are persistent for the life of that instance. Listing 6-11 shows the full text of the updateProps.py script, which contains the updateProps() class. For best results, create a file on the file system called updateProps.py and paste the full text of the code in it:

Listing 6-11. update Props.py creates the updateProps( ) class

import emcli
import re
import operator

class updateProps():
    def __init__(self, agentfilter='.*', typefilter='.*',
                 namefilter='.*', propdict={}):
        self.targs = []
        self.reloadtargs = True
        self.props(propdict)
        self.__loadtargobjects()
        self.filt(agentfilter=agentfilter, typefilter=typefilter,
                  namefilter=namefilter)
    def __loadtargobjects(self):
        if self.reloadtargs == True:
            self.reloadtargs = False
            self.fulltargs = 
              emcli.list(resource='Targets').out()['data']
            self.targprops = 
              emcli.list(resource='TargetProperties'
                        ).out()['data']
    def props(self, propdict):
        assert isinstance(propdict, dict),
               'propdict parameter must be ' + 
               'a dictionary of ' + 
              '{"property_name":"property_value"}'
        self.propdict = propdict
    def filt(self, agentfilter='.*', typefilter='.*',
             namefilter='.*',
             sort=('TARGET_TYPE','TARGET_NAME'), show=False):
        self.targs = []
        __agentcompfilt = re.compile(agentfilter)
        __typecompfilt = re.compile(typefilter)
        __namecompfilt = re.compile(namefilter)
        self.__loadtargobjects()
        for __inttarg in self.fulltargs:
            if __typecompfilt.search(__inttarg['TARGET_TYPE'])
               and __namecompfilt.search(
                   __inttarg['TARGET_NAME'])
               and (__inttarg['EMD_URL'] == None or
               __agentcompfilt.search(__inttarg['EMD_URL'])):
                self.targs.append(__inttarg)
        __myoperator = operator
        for __myop in sort:
            __myoperator = operator.itemgetter(__myop)
        self.targssort = sorted(self.targs, key=__myoperator)
        if show == True:
            self.show()
    def show(self):
        print('%-5s%-40s%s' % (
              ' ', 'TARGET_TYPE'.ljust(40, '.'),
              'TARGET_NAME'))
        print('%-15s%-30s%s %s ' % (
              ' ', 'PROPERTY_NAME'.ljust(30, '.'),
              'PROPERTY_VALUE', '=' * 80))
        for __inttarg in self.targssort:
            print('%-5s%-40s%s' % (
                  ' ', __inttarg['TARGET_TYPE'].ljust(40, '.'),
                  __inttarg['TARGET_NAME']))
            self.__showprops(__inttarg['TARGET_GUID'])
            print('')
    def __showprops(self, guid):
        self.__loadtargobjects()
        for __inttargprops in self.targprops:
            __intpropname = 
              __inttargprops['PROPERTY_NAME'].split('_')
            if __inttargprops['TARGET_GUID'] == guid and
               __intpropname[0:2] == ['orcl', 'gtp']:
                print('%-15s%-30s%s' %
                      (' ', ' '.join(__intpropname[2:]).ljust(
                       30, '.'),
                       __inttargprops['PROPERTY_VALUE']))
    def setprops(self, show=False):
        assert len(self.propdict) > 0,
               'The propdict parameter must contain ' + 
               'at least one property. Use the ' + 
               'props() function to modify.'
        self.reloadtargs = True
        __delim = '@#&@#&&'
        __subseparator = 'property_records=' + __delim
        for __inttarg in self.targs:
            for __propkey, __propvalue
                in self.propdict.items():
                __property_records = __inttarg['TARGET_NAME'] + 
                  __delim + __inttarg['TARGET_TYPE'] + 
                  __delim + __propkey + __delim + __propvalue
                print('Target: ' + __inttarg['TARGET_NAME'] +
                      ' (' + __inttarg['TARGET_TYPE'] +
                      ') Property: '
                      + __propkey + ' Value: ' +
                      __propvalue + ' ')
                emcli.set_target_property_value(
                  subseparator=__subseparator,
                  property_records=__property_records)
        if show == True:
            self.show()

Once there is a file containing the code, we can use this code to update the properties of Enterprise Manager targets. Start a session of EM CLI and establish a login to the OMS.

Using the updateProps( ) Class

Listing 6-12 shows an example of how to use the updateProps() class to update the properties of some EM CLI targets. The class is doing exactly what the commands did in the previous section, but you will notice how much less code is required.

Listing 6-12. Import and use the updateProps( )  class to change target properties

emcli>import updateProps
emcli>myinst = updateProps.updateProps()
emcli>myinst.props({'LifeCycle Status':'Development'})
emcli>myinst.filt(namefilter='^em12cr3.example.com$', typefilter='host')
emcli>myinst.show()
     TARGET_TYPE.............................TARGET_NAME
               PROPERTY_NAME.................PROPERTY_VALUE
================================================================================

     host....................................em12cr3.example.com
               target version................6.4.0.0.0
               os............................Linux
               platform......................x86_64

emcli>myinst.setprops(show=True)
Target: em12cr3.example.com (host)
        Property: LifeCycle Status
        Value: Development

     TARGET_TYPE.............................TARGET_NAME
               PROPERTY_NAME.................PROPERTY_VALUE
================================================================================

     host....................................em12cr3.example.com
               target version................6.4.0.0.0
               os............................Linux
               platform......................x86_64
               lifecycle status..............Development

The example in Listing 6-12 updated one property for one target. The updateProps() class is capable of updating any number of properties for any number of targets. Listing 6-13 expands on the previous example by updating additional properties of the same target.

Listing 6-13. Expand the properties to be updated

emcli>myinst.props({'LifeCycle Status':'Development', 'Location':'COLO', 'Comment':'Test EM'})
emcli>myinst.setprops(show=True)
Target: em12cr3.example.com (host)
        Property: Location
        Value: COLO

Target: em12cr3.example.com (host)
        Property: LifeCycle Status
        Value: Development

Target: em12cr3.example.com (host)
        Property: Comment
        Value: Test EM

     TARGET_TYPE.............................TARGET_NAME
               PROPERTY_NAME.................PROPERTY_VALUE
================================================================================

     host....................................em12cr3.example.com
               target version................6.4.0.0.0
               os............................Linux
               platform......................x86_64
               location......................COLO
               comment.......................Test EM
               lifecycle status..............Development

We did not change the target filter. The only things we changed about the myinst instance were the properties to be updated. The myinst.props() function changed the properties to be applied to the targets. The output showed that three different properties for the same target were updated.

Let’s assume that we want the same three properties applied to additional targets but the only targets we want them applied to are the host and agent targets of the Enterprise Manager OMS server. We will leave the properties alone, but we’ll update the target filter to include the additional target type of  oracle_emd, as shown in Listing 6-14.

Listing 6-14. Change the target type to include the agent target

emcli>myinst.filt(namefilter='^em12cr3.example.com.*$', typefilter='host|oracle_emd')
emcli>myinst.show()
     TARGET_TYPE.............................TARGET_NAME
               PROPERTY_NAME.................PROPERTY_VALUE
================================================================================

     host....................................em12cr3.example.com
               target version................6.4.0.0.0
               os............................Linux
               platform......................x86_64
               location......................COLO
               comment.......................Test EM
               lifecycle status..............Development

     oracle_emd..............................em12cr3.example.com:3872
               os............................Linux
               platform......................x86_64
               target version................12.1.0.3.0
emcli>myinst.setprops(show=True)
Target: em12cr3.example.com (host)
        Property: Location
        Value: COLO

Target: em12cr3.example.com (host)
        Property: LifeCycle Status
        Value: Development

Target: em12cr3.example.com (host)
        Property: Comment
        Value: Test EM

Target: em12cr3.example.com:3872 (oracle_emd)
        Property: Location
        Value: COLO

Target: em12cr3.example.com:3872 (oracle_emd)
        Property: LifeCycle Status
        Value: Development

Target: em12cr3.example.com:3872 (oracle_emd)
        Property: Comment
        Value: Test EM

     TARGET_TYPE.............................TARGET_NAME
               PROPERTY_NAME.................PROPERTY_VALUE
================================================================================

     host....................................em12cr3.example.com
               target version................6.4.0.0.0
               os............................Linux
               platform......................x86_64
               location......................COLO
               comment.......................Test EM
               lifecycle status..............Development

     oracle_emd..............................em12cr3.example.com:3872
               os............................Linux
               platform......................x86_64
               lifecycle status..............Development
               location......................COLO
               comment.......................Test EM
               target version................12.1.0.3.0

The functionality of the updateProps() is extensive and can be as granular or as broad as you would like. A more detailed description of how to use it can be found in Chapter 8. The best way to fully exploit the updateProps() class is to understand the code itself.

Understanding the Code

Now that we’ve seen the code itself as well as an overview of how it works, we’ll analyze the code to understand what is going on behind the scenes. Understanding the code will not only help you exploit all of its functionality, it will also allow you to customize it to your tastes and needs.

The first thing to notice is that everything after the class declaration is indented by at least four spaces. This means that all of the code after the class declaration is part of the class. Within the class are function declarations, and beneath each function is the code that belongs to that function. Let’s break this down into smaller pieces so as to understand what exactly the code is doing and why using a class offers significant advantages.

The first function shown in Listing 6-15 is called __init__(). This is a reserved function name that will automatically be executed when the class is called. The word self is a reference to the instance created from the class. When an instance variable or object (preceded by self.) is created, it is a variable or object assigned to the instance for the life of that instance. When the myinst instance is created from updateProps(), the list object targs and boolean variable reloadtargs are created as well. Instance variables and objects can be viewed as part of the instance in most cases.

Listing 6-15. _init_( ) is the initial function of the updateProps( )  class

def __init__(self, agentfilter='.*', typefilter='.*',
             namefilter='.*', propdict={}):
    self.targs = []
    self.reloadtargs = True
    self.props(propdict)
    self.__loadtargobjects()
    self.filt(agentfilter=agentfilter, typefilter=typefilter,
              namefilter=namefilter)

In Listing 6-16, we create an instance object of the updateProps() class by creating an instance of (executing) the class and assigning that execution to a variable. Then we can print out the reloadtargs instance variable. Notice that the word without parentheses is a class and the same word with parentheses is an instance.

Listing 6-16. Create an instance from the class

emcli>import updateProps
emcli>myinst = updateProps.updateProps()
emcli>print(myinst.reloadtargs)
False
emcli>type(updateProps.updateProps)
<type 'classobj'>
emcli>type(updateProps.updateProps())
<type 'instance'>
emcli>type(myinst)
<type 'instance'>

The last four lines of the __init__ function call the props, __loadtargobjects, and filt functions. The props function, which sets the property keys and values, the __loadtargobjects function, which caches the target information, and the filt function, which allows us to filter the targets assigned to the instance, are defined further down in the class. Creating an instance parses the entire class before processing commands, so the ordering of functions within a class is not important.

The querying of the target information is a fairly expensive process and can take between one and twenty seconds to run. The __loadtargobjects function, as shown in Listing 6-17, is a performance-tuning function that makes the execution of the class more efficient. It only queries and loads the target information on the creation of the instance by being called within the __init__ function, and then only when self.reloadtargs is set to true.

Listing 6-17. The __loadtargobjects( ) of the updateProps( ) class reloads the EM targets when necessary

    def __loadtargobjects(self):
        if self.reloadtargs == True:
            self.reloadtargs = False
            self.fulltargs = 
              emcli.list(resource='Targets').out()['data']
            self.targprops = 
              emcli.list(resource='TargetProperties'
                        ).out()['data']

The props() function shown in Listing 6-18 sets or modifies the propsdict variable, which holds the key value dictionary that is used to set the target properties. This was turned into a function rather than just setting the parameter directly, mainly because it can be called multiple times and includes the assert statement for error checking.

Listing 6-18. The props( ) function of the updateProps( ) class updates the propsdict properties dictionary

    def props(self, propdict):
        assert isinstance(propdict, dict),
               'propdict parameter must be ' + 
               'a dictionary of ' + 
               '{"property_name":"property_value"}'
        self.propdict = propdict

Listing 6-19 shows the filt() function, which defines the scope of targets to which the defined properties will be applied for this instance. Even after the filter is applied, it can be queried or changed. The first three parameters (agentfilter, typefilter, and namefilter) are compiled as regular expressions so as to include or exclude targets for this instance. If the parameters are not defined when the filt() function is called, they are defined implicitly to include all targets. The sort parameter defines how the filtered targets should be sorted, and, finally, the show parameter determines if the output of the filtered target list will be printed to screen.

Listing 6-19. The filt( ) function of the updateProps( ) class creates and manages the filtered list of targets

def filt(self, agentfilter='.*', typefilter='.*',
         namefilter='.*',
         sort=('TARGET_TYPE','TARGET_NAME'), show=False):
    self.targs = []
    __agentcompfilt = re.compile(agentfilter)
    __typecompfilt = re.compile(typefilter)
    __namecompfilt = re.compile(namefilter)
    self.__loadtargobjects()
    for __inttarg in self.fulltargs:
        if __typecompfilt.search(__inttarg['TARGET_TYPE'])
           and __namecompfilt.search(
               __inttarg['TARGET_NAME'])
           and (__inttarg['EMD_URL'] == None or
           __agentcompfilt.search(__inttarg['EMD_URL'])):
            self.targs.append(__inttarg)
    __myoperator = operator
    for __myop in sort:
        __myoperator = operator.itemgetter(__myop)
    self.targssort = sorted(self.targs, key=__myoperator)
    if show == True:
        self.show()

self.targs = [] defines or clears a list to store the filtered targets. The next three lines compile the regular expressions for the first three filter parameters. self.__loadtargobjects() calls the __loadtargobjects() function to reload the full target list. The for __inttarg in self.fulltargs: loop and its nested if statement create the filtered target list based on the filter parameters. The __myoperator = operator and the following three lines define the sort key and sort the filtered target list. Finally, the filtered, sorted list can be printed to screen.

Unless the target properties defined for the myinst instance should be applied to all of the targets, the filt function needs to be called. When the instance is created, the target list is created as well and includes every target defined in Enterprise Manager. The targs instance list object shows the number of targets currently defined for the instance, as shown in Listing 6-20.

Listing 6-20. When an instance of updateProps( ) is created, it includes all targets

emcli>len(myinst.targs)
29

If this instance filtered list is modified, the length of the targs object will reflect the change, as shown in Listing 6-21.

Listing 6-21. The instance targets are pared down with the filt( ) function

emcli>myinst.filt(namefilter='^orcl.*.example.com')
emcli>len(myinst.targs)
2

The filtering of the target list could also take place during instance creation, as shown in Listing 6-22.

Listing 6-22. The instance targets are pared down as part of the instance creation

emcli>myinst = updateProps.updateProps(namefilter='^orcl.*.example.com')
emcli>len(myinst.targs)
2

We probably wouldn’t want to make target property changes without first knowing the target names and the currently defined properties for those targets. The show() function in Listing 6-23 allows us to see this information, printed in a format that is easier to read than JSON or a Python dictionary.

Listing 6-23. The  show( ) function of the updateProps( ) class previews the instance target list along with their currently allocated properties

def show(self):
    print('%-5s%-40s%s' % (
          ' ', 'TARGET_TYPE'.ljust(40, '.'),
          'TARGET_NAME'))
    print('%-15s%-30s%s %s ' % (
          ' ', 'PROPERTY_NAME'.ljust(30, '.'),
          'PROPERTY_VALUE', '=' * 80))
    for __inttarg in self.targssort:
        print('%-5s%-40s%s' % (
              ' ', __inttarg['TARGET_TYPE'].ljust(40, '.'),
              __inttarg['TARGET_NAME']))
        self.__showprops(__inttarg['TARGET_GUID'])
        print('')

The __showprops() function shown in Listing 6-24 is called from the show() function in a recursive manner to retrieve each target property defined for a target. In other words, the __showprops() function is called once for each target defined for the instance, and the properties defined for the target are stored with it as child records.

Listing 6-24. The __showprops( ) function of the updateProps( ) class is called to retrieve the properties for each target defined in the instance

def __showprops(self, guid):
    self.__loadtargobjects()
    for __inttargprops in self.targprops:
        __intpropname = 
          __inttargprops['PROPERTY_NAME'].split('_')
        if __inttargprops['TARGET_GUID'] == guid and
           __intpropname[0:2] == ['orcl', 'gtp']:
            print('%-15s%-30s%s' %
                  (' ', ' '.join(__intpropname[2:]).ljust(
                   30, '.'),
                   __inttargprops['PROPERTY_VALUE']))

Finally, Listing 6-25 shows the setprops() function, which is called to make the magic happen. At the point that this function is called, the filtered target list has already been defined and the propdict dictionary variable is populated with the property name–value pairs that will be set for the filtered target list.

Listing 6-25. The __setprops( ) function of the updateProps( ) class is called to apply the properties updates

def setprops(self, show=False):
    assert len(self.propdict) > 0,
           'The propdict parameter must contain ' + 
           'at least one property. Use the ' + 
           'props() function to modify.'
    self.reloadtargs = True
    __delim = '@#&@#&&'
    __subseparator = 'property_records=' + __delim
    for __inttarg in self.targs:
        for __propkey, __propvalue
            in self.propdict.items():
            __property_records = __inttarg['TARGET_NAME'] + 
              __delim + __inttarg['TARGET_TYPE'] + 
              __delim + __propkey + __delim + __propvalue
            print('Target: ' + __inttarg['TARGET_NAME'] +
                  ' (' + __inttarg['TARGET_TYPE'] +
                  ') Property: '
                  + __propkey + ' Value: ' +
                  __propvalue + ' ')
            emcli.set_target_property_value(
              subseparator=__subseparator,
              property_records=__property_records)
    if show == True:
        self.show()

The assert statement does some error checking to make sure that there is at least one property name–value pair in the propdict variable to set on the target list. self.reloadtargs = True tells the instance that the target and properties information should be freshly queried from the database after the new target properties have been set. __delim = '@#&@#&&' is a set of characters that should never be found in a target or property name and is used as the delimiter in the emcli.set_target_property_value() function. __subseparator = 'property_records=' + __delim is also used for formatting.

The first for loop iterates through the filtered target list. The second for loop iterates through the propdict variable. Inside the second nested for loop is the emcli.set_target_property_value() function, which actually sets the properties on each target. This set_target_property_value() function is executed once for each property per target, so the total number of executions of this function will be the number of records in the filtered target list multiplied by the number of property name–value pairs in propdict. As each property is set, a result is printed to the screen.

Using a class in Python requires a little extra time and effort up front, but it is well worth it. The code you work so hard to create is scalable, extensible, and reusable. You can reuse code from any classes you create and do not have to worry about testing them again.

Summary

Enterprise Manager is truly a powerful tool. Having a firm grasp of the functions of the graphical interface are essential to using it effectively. EM CLI greatly enhances the ability to take advantage of all of the powerful features of EM. EM CLI isn’t the easiest thing to learn, but having the ability to script and automate activities through it offers a clear advantage for the administrator.

Python is a powerful programming language whose maturity has been tested over the last 20-plus years. EM CLI is written in Jython, the Java implementation of Python. Jython allows the administrator to increase their EM effectiveness by taking advantage of the power and agility of Python alongside EM CLI.

The learning curve of any command-line tool is high, but the Python language and EM CLI are quite intuitive. If you get stuck on syntax or functionality, help is plentiful and easy to come by. This chapter gives examples that were created out of necessity in real-world production environments where it wasn’t feasible to do these tasks through the GUI.

The real-world environments you, the reader, will face may differ from those here, but the principals and much of the code demonstrated in this chapter can be used to create effective and efficient solutions for the Enterprise administrator.

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

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