Logo Oracle Deutschland   DBA Community  -  Juni 2014
Oracle Enterprise Manager Cloud Control 12c: Scripting Modus in EMCLI - Jython für DBAs mit PL/SQL Kenntnissen
von Ralf Durben, Oracle Deutschland B.V. & Co. KG

Das Enterprise Manager Commandline Interface kann seit der Version 12.1.0.3 auch im Scripting Modus verwendet werden. Dazu können Sie, auch interaktive, Skripte mit komplexer Logik erstellen. Die Skriptsprache ist dabei Jython. Diese, für die meisten DBAs unbekannte, Skriptsprache ist eine Variante von Python und wird hier dokumentiert.

Als klassischer DBA habe ich die Sprachen SQL und PL/SQL gelernt. Auch die traditionellen Sprachen wie PASCAL oder C sind mir nicht fremd. Mit Jython oder Python hatte ich, wie viele DBAs, bislang noch nichts zu tun und so stellt sich für mich und viele DBAs die Frage, wie schnell diese Skriptsprache erlernt werden kann. Dieser Community Tipp kann sicherlich keine komplette Schulung ersetzen, gibt aber anhand eines praktischen Beispiels einen schnellen Einstieg in die Materie, sodass Sie bald Ihre eigenen Jython Skripte mit EMCLI nutzen können.

Jython ist eine 3GL Sprache mit Variablen, Zuweisungen, Schleifen und IF-Verzweigungen. Auch Prozeduren können in einem Jython Skript als Hilfsprozeduren definiert werden. Also alles, was man so aus der Welt der 3GL Programmiersprachen (Pascal, C, PL/SQL (der PL Teil)) so kennt. Ein Erlernen dieser Sprache sollte also nicht allzu kompliziert sein. Jython wird vorwiegend als Skriptsprache eingesetzt. In Cloud Control kommt diese Sprache zur Zeit nur für EMCLI zum Einsatz.

Da das Einrücken von Textzeilen eine Bedeutung hat, empfiehlt sich der Einsatz eines Editors, der beim Zeilenumbruch in der nächsten Zeile wieder dort beginnt, wo die Vorgängerzeile begonnen hat, und nicht immer an Position 1. Das können mittlerweile viele Editoren. Ein spezieller Editor zum Erstellen von Jython Skripten ist zwar hilfreich, aber nicht notwendig.

Der Fokus dieses Tipps liegt auf einer einfachen, schnell zu verstehenden Darstellung. Aus diesem Grund wird in den Beispielen auf manche abkürzende Kodierungsmöglichkeit verzichtet. Ein Leser mit detaillierten Jython Kenntnissen wird sicherlich derartige Möglichkeiten kennen und finden. Ich bitte aber um Verständnis, dass "kurz" nicht immer "leicht verständlich" ist.

Dieser Tipp gliedert sich in

Installation von EMCLI für Scripting Modus

Seit Cloud Control 12.1.0.3 gibt es zwei verschiedene Binaries für EMCLI: Die klassische Variante und die neue Variante für den Scripting Modus. Die neue Variante muss entsprechend separat installiert werden. Das Vorgehen ist sehr einfach und wird in der GUI von Cloud Control berschrieben unter "Setup -> Command Line Interface" (Rechte Spalte):



Sie erhalten in dem von Ihnen gewählten Verzeichnis ein Executable namens "emcli". Achten Sie darauf, dass Sie im Folgenden genau dieses Executable ausführen und nicht die klassische Variante, falls diese schon installiert ist.

Der erste Start

Sie können viele Kommandos, die später in einem Skript verwendet werden auch direkt ausführen. So können Sie zunächst einmal einiges testen. Rufen Sie EMCLI auf

$ ./emcli
Oracle Enterprise Manager 12c EMCLI with Scripting option Version 12.1.0.3.0.
Copyright (c) 1996, 2013 Oracle Corporation and/or its affiliates. All rights reserved.

Type help() for help and exit() to get out.

emcli>
Das Banner von EMCLI zeigt an, dass Sie das richtige EMCLI mit Scripting Option gestartet haben. Sie können in Jython einfache Ausgaben mit dem Kommando "print" durchführen:
emcli>print("Hallo Welt")
Hallo Welt
Einfache Berechnungen können Sie direkt eingeben:
emcli>1+2
3
Variablen müssen nicht deklariert, sondern können direkt verwendet werden. Die Zuweisung erfolgt durch ein einfaches = .
emcli>zahl = 1+2
emcli>print zahl
3
Die meisten EMCLI Kommandos können wie eine Prozedur oder wie eine Funktion aufgerufen werden.
login(username="SYSMAN")
oder
ret = login(username="SYSMAN")
Die zweite Variante hat den Vorteil, dass das Ergebnis der Ausführung berücksichtigt werden kann.

Der Versuch des Logins bringt ohne weitere Vorbereitungen Fehlermeldungen. Zum Beispiel weiss EMCLI nicht, an welchem OMS die Anmeldung erfolgen soll.
login(username="SYSMAN")
Error: EM URL is not set. Do set_client_property('EMCLI_OMS_URL', '')
Or set it as environment variable.
Sie sollten daher die folgenden drei Client Properties setzen:
set_client_property('EMCLI_OMS_URL','https://hostname:port/em')
set_client_property('EMCLI_TRUSTALL','TRUE')
set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')
Ein einfaches Anmelden sieht dann also so aus:
set_client_property('EMCLI_OMS_URL','https://hostname:port/em')
set_client_property('EMCLI_TRUSTALL','TRUE')
set_client_property('EMCLI_OUTPUT_TYPE', 'JSON')
ret = login(username="SYSMAN")
Enter password :  *********
Sie werden also nach dem Passwort gefragt, da dieses beim Login nicht angegeben wurde.

Skript: Einloggen in Cloud Control

Das Anmelden an einen OMS innerhalb eines Skripts kann man natürlich auch umfangreicher und komfortabler gestalten. Im obigen Fall endet das Skript, falls ein falsches Passwort eingegeben wurde. Auch sind ansprechende Texte und die Möglichkeit der Wahl des OMS Benutzers sinnvoll.

Ein Jython Skript für EMCLI sollte immer einige Standard-Module beinhalten, die am Anfang des Skripts einbezogen werden. Ihr Skript sollte daher immer beginnen mit

import sys
from emcli import *
import re
from xml.etree import ElementTree as ET
import os.path
import string
import socket
import getpass
Als nächstes brauchen Sie eigentlich immer Hilfsroutinen für die Eingabe von Werten, hier für den Benutzernamen und das Passwort. Diese Hilfsroutinen sehen so aus:

Normale Eingabe mit Vorgabe eines Defaults. Es wird getestet, dass die Eingabe nicht leer ist.
## Routine to get user input - normal data
def get_normal_input(str, default):
    v_input = raw_input(str)
    if default != "" and v_input == "":
        v_input = default
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_normal_input(str, default)
    return v_input
Eingabe eines Passworts, wobei die Eingabe nicht angezeigt wird. Es wird getestet, dass die Eingabe nicht leer ist.
## Routine to get user input - password data
def get_password(str):
    v_input = getpass.getpass()
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_password(str)
    return v_input
Anhand dieser Beispiele können Sie erkennen, wie die Operatoren != (ungleich) und == (gleich) verwendet werden. Auch die Verwendung des Programmiersprachenkonstrukts "if" kommt selbst dem reinen PL/SQL-Programmierer bekannt vor. Es fällt aber auf, dass kein Semikolon als Zeilenabschluß verwendet wird. Das Einrücken des Textes ist entscheidend für die Zuordnung der jeweiligen Kommandos zu den Verzweigungen, beziehungsweise Bildung von Kommandoblöcken.

An dieser Stelle soll gezeigt werden, wie in einem Skript die Anmeldung an den OMS solange wiederholt wird, bis korrekte Credentials angegeben werden. Dazu wird eine weitere Prozedur zum Login erstellt.
def login_to_em():
 set_client_property('EMCLI_OMS_URL','https://hostname:port/em')
 set_client_property('EMCLI_TRUSTALL','TRUE')
 set_client_property('EMCLI_OUTPUT_TYPE','JSON')
 omsusername = get_normal_input("\nOMS Login, Benutzername:[sysman] ", "sysman")
 omspassword = get_password("OMS Login, Passwort    : ")
 try:
  ret = login(username=omsusername, password=omspassword)
  print("Logged in")
  return True
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  return False
Zunächst werden die Client Properties gesetzt. Im Beispiel soll der OMS fest kodiert sein. Dann fragt die Prozedur einen Benutzernamen und ein Passwort unter Verwendung der obigen Prozeduren ab. Die jeweiligen Eingaben werden in Variablen gespeichert.

Der Anmeldevorgang könnte jetzt direkt mit login(...) erfolgen. Falls dieses aber fehlschlägt erfolgt eine Fehlermeldung, die im Skript nicht abgefangen wird, und das Skript endet. Gewünscht ist aber, dass es dann einen zweiten Versuch geben wird. Dafür können Sie mit dem Konstrukt "try" arbeiten. Wieder gibt das Einrücken die Kommandoblöcke an.

Wenn der Login erfolgreich ist, gibt das Skript per "print" ein "Logged in" aus. Bei einem Fehler wird der Fehlerbehandlungsteil aufgerufen. Dort kann mit str(e) auf die Original Fehlermeldung zugegriffen werden (wird hier einfach ausgegeben). Sie können die Ausgabe dann in jede Sprache übersetzen.

Die Prozedur/Funktion gibt im Falle eines Fehlers ein "False" zurück. Im Falle eines erfolgreichen Logins ist der Rückgabewert "True".

Jedes Jython Skript besteht aus dem Include-Bereich (siehe oben), dem Hilfsprozedur-Bereich und dem Hauptbereich, der sich gleich nach dem Hilfsprozedur-Bereich anschließt. Im vorliegenden Beispiel soll der Anmeldevorgang solange wiederholt werden, bis er erfolgreich ist (oder anders ausgedrückt: solange der Returnwert der Hilfsprozedur False ist).
while (login_to_em() == False):
  donothing=1
In einer While-Schleife (kennt man auch aus PL/SQL) wird diese wiederholt, so lange die Hilfprozedur den Wert "False" zurück liefert. Beachten Sie, dass die Prozedur im Vergleich ausgeführt wird. Der While-Körper ist eigentlich leer, hier wird eine Dummy-Zuweisung verwendet.

Im Ganzen sieht das Skript jetzt so aus:
	      
import sys
from emcli import *
import re
from xml.etree import ElementTree as ET
import os.path
import string
import socket
import getpass


## Routine to get user input - normal data
def get_normal_input(str, default):
    v_input = raw_input(str)
    if default != "" and v_input == "":
        v_input = default
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_normal_input(str, default)
    return v_input

## Routine to get user input - password data
def get_password(str):
    v_input = getpass.getpass()
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_password(str)
    return v_input

def login_to_em():
 set_client_property('EMCLI_OMS_URL','https://sccloud046.de.oracle.com:7799/em')
 set_client_property('EMCLI_TRUSTALL','TRUE')
 set_client_property('EMCLI_OUTPUT_TYPE','JSON')
 omsusername = get_normal_input("\nOMS Login, Benutzername:[sysman] ", "sysman")
 omspassword = get_password("OMS Login, Passwort    : ")
 try:
  ret = login(username=omsusername, password=omspassword)
  print("Logged in")
  return True
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  return False

while (login_to_em() == False):
  donothing=1    

Skript: Löschen eines nicht mehr laufenden Agenten inklusive aller Targets

Verwenden Sie das obige Skript, welches die Anmeldung an den OMS beinhaltet und erweitern Sie dieses für Ihre sonstigen Ziele. Im folgenden Beispiel soll eine Liste aller Agenten angezeigt werden, die nicht im Status "UP" sind. Dann kann ein Agentenname angegeben werden. Dieser Agent soll dann gelöscht werden, inklusive aller Targets, die von diesem Agenten überwacht werden. Am Ende wird dann eine aktualisierte Agentenliste angezeigt.

Zum Anzeigen der Agenten verwenden Sie das EMCLI Verb "get_targets" zusammen mit dem Paramater "targets=". Die Liste soll eine Überschrift tragen

print("\nListe aller Agenten, die nicht UP sind:")
print("---------------------------------------")
Aufruf des EMCLI Verbs:
emdtyp = "oracle_emd"
hostlist = get_targets(targets=emdtyp)
Das Ergebnis ist jetzt als Objekt in der Variable "hostlist" gespeichert. Zur Ausgabe greifen Sie über alle Zeilen auf die Spalten "Status" und "Target Name" zu. Die Namen dieser Spalten erhalten Sie, indem Sie das EMCLI Verb einfach mal direkt aufrufen
emcli>emdtyp = "oracle_emd"
emcli>get_targets(targets=emdtyp)
{'data': [
{'Status': 'Up', 'Target Name': 'sccloud028', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud046', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud032', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'radu1', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud049', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud026', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud010', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud014', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud050', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Agent Unreachable', 'Target Name': 'sccloud020', 'Status ID': '4', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}, 
{'Status': 'Up', 'Target Name': 'sccloud003', 'Status ID': '1', 'Warning': '0', 'Critical': '0', 'Target Type': 'oracle_emd'}]}
Gehen Sie also durch die Ergebnisliste mit
for hostlist in hostlist.out()['data']:
  if hostlist['Status'] == "Up":
     donothing = 1
  else:
     print(hostlist['Status'] + "   " + hostlist['Target Name'])
Neben einer "while"-Schleife gibt es in Jython also auch die in PL/SQL bekannte "for"-Schleife. Auch hier bestimmt das Einrücken, welche Kommandos zu einem Kommandoblock gehören. Im obigen Beispiel wurde im IF-Kommando zunächst mit "gleich" gearbeitet. Das geht natürlich auch kürzer mit
for hostlist in hostlist.out()['data']:
  if hostlist['Status'] != "Up":
     print(hostlist['Status'] + "   " + hostlist['Target Name'])
Nach der Anzeige der Agenten soll nun ein Agent zum Löschen angegeben werden. Dazu wird wieder die Hilfprozedur "get_normal_input" verwendet:
agent_to_remove = get_normal_input("\nAgent, der geloescht werden soll: ","")
try:
 ret = delete_target(name=agent_to_remove,type=emdtyp,delete_monitored_targets=True)
 print (ret)
except emcli.exception.VerbExecutionError, e:
 print (str(e))
Nach der Eingabe des Agentennamens, wird durch die Verwendung von "try" das Löschen des Agenten wieder mit einer Fehlerbehandlung versehen. Wenn also zum Beispiel ein nicht existierender Agent eingegeben wird, sieht die Fehlermeldung ordentlicher aus, als würde das Skript mit der Standard Fehlermeldung aussteigen.

Im Ganzen sieht das Skript jetzt so aus:
#########################################################################
# emcli Jython script zum Loeschen eines Agenten mit all seinen Targets # 
#   The agent must be down                                              #
#                                                                       #
# Autor    : Ralf Durben                                                #
# Datum    : 06.01.2014                                                 #
# Changelog:                                                            #
#########################################################################

import sys
from emcli import *
import re
from xml.etree import ElementTree as ET
import os.path
import string
import socket
import getpass

## Routine fuer Benutzereingabe - normale Daten
def get_normal_input(str, default):
    v_input = raw_input(str)
    if default != "" and v_input == "":
        v_input = default
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_normal_input(str, default)
    return v_input

## Routine fuer Benutzereingabe - Passwort Daten
def get_password(str):
    v_input = getpass.getpass()
    if v_input == "":
       print("Geben Sie einen nichtleeren Wert ein: ")
       v_input = get_password(str)
    return v_input

## Routine zum Anmelden an den OMS    
def login_to_em():
 set_client_property('EMCLI_OMS_URL','https://sccloud046.de.oracle.com:7799/em')
 set_client_property('EMCLI_TRUSTALL','TRUE')
 set_client_property('EMCLI_OUTPUT_TYPE','JSON')
 omsusername = get_normal_input("\nOMS Login, Benutzername:[sysman] ", "sysman")
 omspassword = get_password("OMS Login, Passwort    : ")
 try:
  ret = login(username=omsusername, password=omspassword)
  print("Logged in")
  return True
 except emcli.exception.VerbExecutionError, e:
  print (str(e))
  return False


#### Hier beginnt der Hauptbereich

### Anmelden an den OMS
while (login_to_em() == False):
  donothing=1

### Anzeige aller Agenten, die nicht UP sind
print("\nListe aller Agenten, die nicht UP sind:")
print("---------------------------------------")
emdtyp = "oracle_emd"
hostlist = get_targets(targets=emdtyp)
for hostlist in hostlist.out()['data']:
  if hostlist['Status'] != "Up":
     print(hostlist['Status'] + "   " + hostlist['Target Name'])
print ("\n")

### Loeschen eines Agenten mit all seinen Targets
agent_to_remove = get_normal_input("\nAgent, der geloescht werden soll: ","")
try:
 ret = delete_target(name=agent_to_remove,type=emdtyp,delete_monitored_targets=True)
 print (ret)
except emcli.exception.VerbExecutionError, e:
 print (str(e))

### Anzeige aller Agenten, die nicht UP sind
print("\n Neue Liste aller Agenten, die nicht UP sind:")
print("---------------------------------------------")
emdtyp = "oracle_emd"
hostlist = get_targets(targets=emdtyp)
for hostlist in hostlist.out()['data']:
  if hostlist['Status'] != "Up":
     print(hostlist['Status'] + "   " + hostlist['Target Name'])
Sie können dieses Beispiel gerne für die eigene Nutzung kopieren und beliebig erweitern. Dazu verweise ich nochmals auf die
Dokumentation von Jython. Ich hoffe, dass Ihnen dieser Tipp zeigen konnte, dass der Einstieg in die Scripting Option von EMCLI auch für PL/SQL Programmierer und DBAs schnell möglich ist.

Optimierung: Minimierung des Konstrukts "try"

Wenn Sie in Ihrem Jython-Skript mehrere Aufrufe von EMCLI-Verben (Kommandos) verwenden, die jeweils ordentliche Erfolgs- oder Fehlermeldungen liefern sollen, würden Sie vielfach das "try"-Konstrukt nutzen. Dieses läßt sich optimieren, indem eine Prozedur dafür erstellt wird:

## Routine to execute emcli commands with proper exception output
def execute_command(commandstring):
 try:
  exec "ret = " + commandstring
  print (ret)
 except emcli.exception.VerbExecutionError, e:
  print (str(e)) 
Sie nutzen die EMCLI Kommandos dann statt zum Beispiel
try:
 ret = delete_target(name=agent_to_remove,type=emdtyp,delete_monitored_targets=True)
 print (ret)
except emcli.exception.VerbExecutionError, e:
 print (str(e)) 
wie folgt:
execute_command('delete_target(name=agent_to_remove,type=emdtyp,delete_monitored_targets=True)')
Sie können das EMCLI Kommando also zunächst als String zusammensetzen und dann über die Prozedur ausführen.

Update: Wichtige Änderungen in 12c Release 4

In Release 4 gibt es eine wichtige Änderung für die Eingabe von Passwörtern, da die Klasse "getpass" nicht mehr funktioniert. Stattdessen gibt es eine neue Klasse "Password". Die Funktion "get_password" sieht also so aus:

## Read Password
def get_password(msg):
 while(True):
   read = Password.readPassword(msg)
   password = String(read.getPassword())
   if str(password) == "":
      print("Geben Sie einen nichtleeren Wert ein: ")
   else:
      return str(password)
Folgende Imports müssen zusätzlich in den Anfang des Skripts übernommen werden:
import oracle.sysman.emSDK.emCLI.Password as Password
import java.lang.String as String
Der Vorteil liegt darin, dass die Eingabe jetzt mit Sternchen bestätigt wird.

Lizenzierung

Die Nutzung von EMCLI ist im Prinzip in der Basisfunktionalität von Cloud Control enthalten. Die Nutzung einzelner Kommandos in EMCLI können jeweils durch ein Management Pack lizenzpflichtig sein. Beispiel: Starten von Deployment Prozeduren, die ihrerseits Bestandteil des Lifecycle Management Packs sind.

Weitere Informationen


Zurück zum Anfang des Artikels

Zurück zur Community-Seite