WebSphere Application Server v7 – AdminConfig

Architecture starts when you carefully put two bricks togehter.” – Ludwig Mies van der Rohe

Dank des wsadmin Tools ist es möglich, die Konfiguration des WebSphere Application Server zu adaptieren. Es ist sogar möglich, die Konfiguration zu verändern während der Server gestoppt ist. Allerdings muss man hierzu die Funktionsweise des AdminConfig Scripting-Objekts verstehen.

Überblick

Wenn man wsadmin gestartet hat und folgendes Kommando eingibt AdminConfig.help(), erhält man einen Überblick über alle Funktionen die von dem Management-Objekt unterstützt werden. Einige davon sind folgende:

  • List
  • Create
  • Remove
  • Display
  • Modify

WARNUNG Wenn man die WebSphere Application Server Konfiguration mittels Scripting verändert, kann es dazu führen, dass die Konfiguration ungültig wird. Man sollte daher bei der Manipulation der Konfiguration sehr sorgfältig sein. Man sollte sich auf jeden Fall mit dem backupConfig-Verwaltungsscript vertraut machen, bevor man die Konfiguration verändert.

Eines ist ganz klar, das Erlernen der Administration des Application Servers mittels wsadmin ist nicht gerade trivial. Das schwierigste Problem ist herauszufinden, welches Administrationsobjekt für eine bestimmte Aufgabe zuständig ist. Manche benötigen einen Configuration Identifier (config ID), andere wiederum benötigen einen MBean Identifier. Woher soll man nun wissen, welcher wann und wie zu verwenden ist?

Configuration Identifier (config ID)

Die intuitivste Antwort ist, überall dort wo man die Konfiguration verändert, benötigt man einen Configuration Identifier und überall dort wo man aktive Application Server Ressourcen bearbeitet, benötigt man MBean Identifier. Die folgende Abbildung zeigt die Config-IDs für die existieren Server auf meinem Notebook:

image

In dieser Konfigurations-ID kann man die Verzeichnisstruktur der Application Server Installation ablesen. Unter <SERVER_ROOT>/profiles/<PROFILE_NAME>/config findet man diese Verzeichnisse:

image

Folgt man den Pfadangaben in der Config-ID, dann findet man die jeweilige XML-Datei, die die Konfiguration für den entsprechenden Server darstellt:

image

Bei der ID fällt sofort dieses seltsame Anhängsel auf: #Server_1183121908656. Dabei handelt es sich um ein konkretes Subelement des process:Server Tag innerhalb der Konfiguration:

image

Containment Path

Eine häufig auftretende Anforderung ist das Ermitteln einer Config-ID für ein bestimmtes Konfigurationsobjekt. In der Dokumentation ist die am häufigsten verwendete Technik, die Verwendung der Methode AdminConfig.getid() um ein Config-ID zu ermitteln. Allerdings ist hierzu der so genannte Containment Path erforderlich.

image

Der Containment Path besteht aus mehreren name/value Paaren, die in einer bestimmten Reihenfolge angegeben werden müssen. Der name-Teil ist obligatorisch. Es handelt sich dabei um einen Configuration-Object-Type gefolgt von einem Doppelpunkt und einem optionalen Wert. Die name/value Paare werden durch einen ‘/’ getrennt. Einige Beispiele wären: ‘/Cell:/’, oder ‘/Cell:myCellName/’.

Wenn man jetzt zum Beispiel die Cell-ID oder die Node-ID ermitteln möchte, muss man zuerst die Cell und den Node über das AdminControl-Objekt beziehen und kann dann diese in den Containment Path einbinden:

image

Configuration Types

Was für Konfigurationstypen gibt es eigentlich? Ein einfacher Aufruf der Methode AdminConfig.types() über wsadmin verrät mehr. In der Version 7 des WebSphere Application Servers gibt es nahezu 800 verschiedene Typen. Es ist also klar dass man nicht alle diese Typen je perfekt beherrschen wird. Neben der getid()-Methode gibt es mit der AdminConfig.list() Methode eine weitere Möglichkeit Config-IDs zu ermitteln. Zum Beispiel kann man mittels AdminConfig.list("Server”) die Config-ID für die installierten Server ermitteln, wobei ‘Server’ einen gütligen Konfigurationstyp darstellt:

image

Diese Methode erscheint mir etwas einfacher also die Verwendung der getid()-Methode mit dem entsprechenden Containment Path. Allerdings kommt es natürlich darauf an, was für eine WebSphere Application Server Version man installiert hat (Managed, Unmanaged, …). Der offensichtliche Unterschied der beiden Varianten zur Ermittlung der Config-ID sind die zu übergebenen Parameter, nämlich bei getid() der Containment Path und bei list() ein bestimmter Konfigurationstyp (‘Server’).

Zum Beispiel erzeugen die Aufrufe AdminConfig.list("Server”) und AdminConfig.getid(“/Server:/”) genau den gleichen Output:

image

TIPP Mittels splitlines() wird das Ergebnis in eine Liste konvertiert und man kann dann mittels Index auf die einzelnen Ergebnisse zugreifen.

image

Verwendung einer Config-ID

Was kann man nun mittels einer Config-ID erledigen? Eine der brauchbarsten Methoden von AdminConfig ist AdminConfig.show(), welche Informationen über existierende Attribute und Werte der Ressource, die mittels der Config-ID ermittelt wurde, ausgibt. Das folgende Beispiel fragt zuerst Information über die show()-Methode ab und gibt dann alle Informationen über einen meiner installierten Server (server1) aus:

image

Ein weiteres Beispiel ermittelt die Attribute für die gesetzten Umgebungsvariablen für den server1. Zuerst speichere ich mir eine Config-ID einer Umgebungsvariable in einer Hilfsvariable ab und übergebe diese dann an die AdminConfig.show() Methode.

image

Leider ist der Output dieses Beispiels eher unschön. Deshalb habe ich eine Methode showAsDict(‘<CONFIG_ID>’) implementiert die den Output in einem Dictonary abspeichert. Danach genießt man alle Vorzüge dieser Datenstruktur. Der folgende Codeabschnitt zeigt diese Methode:


def showAsDict( configID ) :
  'Return a dictionary of the AdminConfig.show( configID ) result.'
  result = {}
  try :
    #-----------------------------------------------------------------
    # The result of the AdminConfig.show() should be a string
    # containing many lines.  Each line of which starts and ends
    # with brackets.  The "name" portion should be separated from the
    # associated value by a space.
    #-----------------------------------------------------------------
    for item in AdminConfig.show( configID ).splitlines() :
      if ( item[ 0 ] == '[' ) and ( item[ -1 ] == ']' ) :
        ( key, value ) = item[ 1:-1 ].split( ' ', 1 )
        result[ key ] = value
  except NameError, e :
    print 'Name not found: ' + str( e )
  except :
    ( kind, value ) = sys.exc_info()[ :2 ]
    print 'Exception  type: ' + str( kind )
    print 'Exception value: ' + str( value )
  return result

Diese Methode kann wie folgt angewendet werden. Im Anschluss befindet sich dann der verbesserte Output:


dict = showAsDict(serverConfigId)
for name in 'name,serverType'.split(','):
    print '%-10s : %s' % (name, dict[name])

 image 

“Show” und “Tell” Methoden

Neben der show()-Methode gibt es noch weitere Möglichkeiten, sich Informationen eines bestimmten Typs, an dem man gerade arbeitet, anzeigen zu lassen. Zum Beispiel kann man mittels der attributes()-Methode alle zugehörigen Attribute für einen bestimmten Typ anzeigen lassen. Die attributes()-Methode benötigt als Parameter einen Konfigurationstyp (AdminConfig.types()).  Wenn man sich mittels der help()-Methode des AdminConfig Objekts die Beschreibungen der beiden Methoden ausgeben lässt, sieht man folgendes:

image

Zum Beispiel kann man sich alles Attribute von Umgebungsvariablen wie folgt anzeigen lassen.

image

print AdminConfig.attributes('VariableSubstitutionEntry')

Im folgenden etwas komplizierteren Beispiel, zeige ich die Möglichkeit wie man die beiden Methoden show() und attributes() so kombinieren kann, um herauszufinden, welche Attribute nicht beim Output der show()-Methode angezeigt werden, bezogen auf einen meiner Application Server.


import re

server1ConfigId = AdminConfig.list('Server').splitlines()[1]
serverDict = showAsDict(server1ConfigId)

regExPattern = re.compile('^(\w+)(?:\s+.*)$', re.MULTILINE)

aNames = regExPattern.findall(AdminConfig.attributes('Server'))

missing = [x for x in aNames if not serverDict.has_key(x)]

for name in missing:
	try:
	       attribute = AdminConfig.showAttribute(server1ConfigId, name)
	except:
	       attribute = ''
	print '%-20s %s' % (name, attribute)

Und der dazugehörige Output inklusive Header:

image

Interessant am letzen Beispiel ist die Zeile, die das Pattern für eine Regular Expression implementiert. Es extrahiert Attributnamen von mehrzeiligen Strings und wird dann mittels findall() Methode, auf den Attributen die mittels attributes() Methode des AdminConfig bezogen werden, angewendet. Damit werden alle Attributnamen der attributes() Methode gefiltert. In der darauffolgenden Zeile wird mittels ‘list comprehension’ eine Liste erstellt, die alle Attributnamen enthält, die nicht im Output der show() Methode (in showAsDict() implementiert) vorkommen. Diese Liste wird in der Variable missing gespeichert. In der anschließenden for-Schleife wird dann versucht, den Wert des fehlenden Attributes anzuzeigen (mittels showAttribute() Methode).

D.h. die ausgegebenen Attribute kommen zwar in der attributes() jedoch nicht in der show() Methode vor. Auf jeden Fall steht bis zu diesem Zeitpunkt nicht fest, warum die attributes(), die defaults() und die required() Methoden so unterschiedliche Ergebnisse zurückliefern. Für manche Attribute sind bestimmte Default-Values gesetzt, für manche nicht. Warum kann man aufgrund der Hilfe leider nicht herausfinden.

Methoden zum Erstellen und Modifizieren

Das AdminConfig Objekt bietet die Möglichkeit der Erstellung und Modifizierung von Konfigurationsobjekten. Zuerst möchte ich gerne zeigen, wie man ein solches erstellt. Dazu betrachte ich zuerst einmal die Beschreibung der AdminConfig.create() Methode mittels help().

image

Folgende Parameter sind notwendig: Type, Parent und Attribute. Type sollte klar sein, dieser identifiziert die Art des zu erstellenden Objekts. Aber bitte was bedeutet der Parent Parameter? Dazu muss man wissen, dass man keine stand-alone Konfigurationsobjekte erstellen kann. Jedes Objekt hat einen bestimmen Platz in der Konfigurationshierarchie. Der Parent Parameter ist also eine Config-ID eines existierenden Objekts, dass das neu zu erstellende Objekt beinhalten kann. D.h. jeder Objekttyp kann nur an bestimmten Plätzen in der Objekthierarchie vorkommen.

Wie kann man nun herausfinden welche Objekte ein bestimmtes Objekt beinhalten darf. Anders ausgedrückt, wie kann man die zulässigen Parent Objekte bestimmen?  Ja, natürlich gibt es einen Weg, und zwar muss man der AdminConfig.parents() Methode einen Konfigurationstyp als Parameter übergeben und erhält dann alle zulässigen Objekte. Folgendes Beispiel gibt alle zulässigen Container für das Server Konfigurationsobjekt aus:

image

Um ein neues Server Konfigurationsobjekt zu erstellen, muss man daher entweder die Config-Id eines Nodes oder eines Server-Clusters angeben (AdminConfig.create(“Server”, <NODE|CLUSTER_CONFIG_ID>, <ATTRIBUTES>)).

Natürlich ist es jetzt auch möglich mittels der parents() und der types() Methode, alle gültigen parent/child-Beziehungen herauszufinden.

Zuerst muss man herausfinden, was von der AdminConfig.parents() Methode für ein spezifiziertes Objekt zurückgegeben wird, für das es noch kein Parent Objekt gibt (z.B.: beim VariableSubstitutionEntry) . Wenn man das einmal ermittelt hat, kann man sich ein einfaches Skript erstellen, um eine Liste aller Namen von Konfigurationstypen zu generieren. Für jeden Typ kann man dann den gültigen Parent-Typ, der das aktuelle Objekt enthalten kann, bestimmen.

types = AdminConfig.types().splitlines()
for kind in types :
  parents = AdminConfig.parents( kind )
  if not parents.startswith( 'WASX7351I' ) :
    print '%s : %s' % ( kind, parents.splitlines() )

Hat man also nun verstanden was die einzelnen Parameter der AdminConfig.create() Methode bedeuten, kann man wie folgt vorgehen um ein bestimmtes Konfigurationsobjekt neu zu erstellen:

  1. Bestimme den Typ des zu erstellenden Objekts
  2. Suche diesen Typ mittels AdminConfig.types() Methode
  3. Überprüfe ob es ein passendes Parent-Objekt gibt mittels AdminConfig.parents() Methode
  4. Ermittle die zulässigen Attribute (Standard, Obligatorisch, Zulässig) mittels der required(), defaults() und der attributes() Methoden
  5. Bestimme das Parent-Objekt und dessen Config-Id
  6. Lege die gewünschten Parameter fest, die bei der Erstellung notwendig sind
  7. Erstelle das Objekt mittels create() Methode

Zum Erstellen eines neuen Servers könnte folgender Code verwendet werden:

node = AdminConfig.getid(‘/Node:/’).splitlines()[-1]
attr = [[‘name’,’serverName’]]
server = AdminConfig.create( ‘Server’, node, attr )

Nachdem man Änderungen an der Konfiguration durchgeführt hat, und man sicher ist, dass diese Konfigurationen zulässig sind, muss man diese entweder mittels AdminConfig.save() speichern oder mit AdminConfig.reset() zurücksetzen. Wenn man aus wsadmin einfach mit quit aussteigt werden die durchgeführten Änderungen ebenfalls verworfen.

Erzeugen einer neuen WebSphere Variable

Der eigentliche Grund warum ich das AdminConfig näher untersucht habe, war das automatisierte Erstellen von sogenannten WebSphere Variablen. Wie habe ich nun herausgefunden, dass ich für die Erzeugung einer WebSphere Variable das AdminConfig Objekt benötige? Immer wenn ich etwas bezüglich des IBM WebSphere Application Server automatisieren möchte, und nicht weiß was ich dazu benötige, versuche ich zuerst diesen Vorgang in der WebSphere Administrationskonsole nachzuspielen und mittels Befehlsunterstützung lasse ich mir das Kommando anzeigen:

image image

Da es leider keine Hilfe beim Anlegen einer WebSphere Variable gibt, habe ich dann das AdminConfig Objekt näher unter die Lupe genommen. Bei dem ausgegeben Kommando kann man auch sehen, dass der Konfigurationstyp ‘VariableSubstitutionEntry’ verwendet wird, um die Liste aller Variablen anzuzeigen. D.h. ich habe im nächsten Schritt mittels AdminConfig.types() überprüft, ob es sich tatsächlich um ein Konfigurationsobjekt handelt.

Danach bin ich einfach die zuvor beschriebenen 7 Schritte zur Erstellung eines Konfigurationsobjekts durchgegangen. Allerdings gab es schon beim 3. Schritt Probleme, da ich mittels AdminConfig.parents() kein passendes Parent Objekt finden konnte. Also hab ich noch einmal die Typen mittels AdminConfig.types() näher betrachtet und festgestellt, dass es einen Typ VariableMap gibt. Lässt man sich mittels AdminConfig.attribtutes(“VariableMap”) die zugehörigen Attribute des Objekts anzeigen, stellt man fest, dass diese eine Referenz auf VariableSubstitutionEntry besitzt.

Intuitiv habe ich daher die 7 Schritte Strategie fortgesetzt und eine VariableMap als Parent angegeben. Dazu hab ich zuerst die passende Config-Id der VariableMap bestimmten müssen. Wie schon weiter oben beschrieben, kann diese mittels der AdminConfig.list() Methode bezogen werden. Als Parameter habe ich hierfür den Konfigurationstyp (VariableMap) und den Scope (Config-Id des Servers) übergeben.

server = AdminConfig.list("Server").splitlines()[1]
varMap = AdminConfig.list("VariableMap", server)
AdminConfig.create('VariableSubstitutionEntry', varMap, [['description', 'Description1'], ['symbolicName', 'user.name'], ['value', 'Claus Polanka']])
AdminConfig.save()

Zuletzt muss die Änderung noch gespeichert werden und kann danach in der Administrationskonsole kontrolliert werden.

image

Über sageniuz

https://about.me/ClausPolanka
Dieser Beitrag wurde unter Programming veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s