To access IceGrid's administrative facilities from a program, you must first establish an administrative session. Once done, a wide range of services are at your disposal, including the manipulation of IceGrid registries, nodes, and servers; deployment of new components such as well-known objects; and dynamic monitoring of IceGrid events.

Note that, for replicated registries, an administrative session can be established with either the master or a slave registry replica, but a session with a slave replica is restricted to read-only operations.

On this page:

Creating an Administrative Session

The Registry interface provides two operations for creating an administrative session:

{zcode:slice}
module IceGrid {
    exception PermissionDeniedException {
        string reason;
    };

    interface Registry {
        AdminSession* createAdminSession(string userId, string password)
            throws PermissionDeniedException;

        AdminSession* createAdminSessionFromSecureConnection()
            throws PermissionDeniedException;

        idempotent int getSessionTimeout();

        // ...
    };
};
{zcode}

The createAdminSession operation expects a username and password and returns a session proxy if the client is allowed to create a session. By default, IceGrid does not allow the creation of administrative sessions. You must define the property IceGrid.Registry.AdminPermissionsVerifier with the proxy of a permissions verifier object to enable session creation with createAdminSession. The verifier object must implement the interface Glacier2::PermissionsVerifier.

The createAdminSessionFromSecureConnection operation does not require a username and password because it uses the credentials supplied by an SSL connection to authenticate the client. As with createAdminSession, you must configure the proxy of a permissions verifier object before clients can use createAdminSessionFromSecureConnection to create a session. In this case, the IceGrid.Registry.AdminSSLPermissionsVerifier property specifies the proxy of a verifier object that implements the interface Glacier2::SSLPermissionsVerifier.

As an example, the following code demonstrates how to obtain a proxy for the registry and invoke createAdminSession:

{zcode:cpp}
Ice::ObjectPrx base = communicator->stringToProxy("IceGrid/Registry");
IceGrid::RegistryPrx registry = IceGrid::RegistryPrx::checkedCast(base); 
string username = ...; 
string password = ...; 
IceGrid::AdminSessionPrx session; 
try { 
    session = registry->createAdminSession(username, password); 
} catch (const IceGrid::PermissionDeniedException & ex) { 
    cout << "permission denied:\n" << ex.reason << endl; 
}
{zcode}

The AdminSession interface provides operations for accessing log files and establishing observers. Furthermore, two additional operations are worthy of your attention:

{zcode:slice}
module IceGrid {
    interface AdminSession extends Glacier2::Session {
        idempotent void keepAlive();
        idempotent Admin* getAdmin();
        // ...
    };
};
{zcode}

If your program uses an administrative session indefinitely, you must prevent the session from expiring by invoking keepAlive periodically. You can determine the timeout period by calling getSessionTimeout on the Registry interface. Typically a program uses a dedicated thread for this purpose.

The getAdmin operation returns a proxy for the IceGrid::Admin interface, which provides complete access to the registry's settings. For this reason, you must use extreme caution when enabling administrative sessions.

Accessing Log Files Remotely

IceGrid's AdminSession interface provides operations for remotely accessing the log files of a registry, node, or server:

{zcode:slice}
module IceGrid {
interface AdminSession extends Glacier2::Session {
    // ...

    FileIterator* openServerLog(string id, string path, int count)
        throws FileNotAvailableException, ServerNotExistException,
               NodeUnreachableException, DeploymentException;
    FileIterator* openServerStdErr(string id, int count)
        throws FileNotAvailableException, ServerNotExistException,
               NodeUnreachableException, DeploymentException;
    FileIterator* openServerStdOut(string id, int count)
        throws FileNotAvailableException, ServerNotExistException,
               NodeUnreachableException, DeploymentException;

    FileIterator* openNodeStdErr(string name, int count)
        throws FileNotAvailableException, NodeNotExistException,
               NodeUnreachableException;
    FileIterator* openNodeStdOut(string name, int count)
        throws FileNotAvailableException, NodeNotExistException,
               NodeUnreachableException;

    FileIterator* openRegistryStdErr(string name, int count)
        throws FileNotAvailableException,
               RegistryNotExistException,
               RegistryUnreachableException;
    FileIterator * openRegistryStdOut(string name, int count)
        throws FileNotAvailableException,
               RegistryNotExistException,
               RegistryUnreachableException;
};
};
{zcode}

In order to access the text of a program's standard output or standard error log, you must configure it using the Ice.StdOut and Ice.StdErr properties, respectively. For registries and nodes, you must define these properties explicitly but, for servers, the node defines these properties automatically if the property IceGrid.Node.Output is defined, causing the server's output to be logged in individual files.

If IceGrid.Node.Output is not defined, the following rules apply:

Log messages from the node itself are sent to stderr unless you set Ice.UseSyslog (for Unix). If the node is started as a Windows service, its log messages always are sent to the Windows event log.

In the case of openServerLog, the value of the path argument must resolve to the same file as one of the server's log descriptors. This security measure prevents a client from opening an arbitrary file on the server's host.

All of the operations accept a count argument and return a proxy to a FileIterator object. The count argument determines where to start reading the log file: if the value is negative, the iterator is positioned at the beginning of the file, otherwise the iterator is positioned to return the last count lines of text.

The FileIterator interface is quite simple:

{zcode:slice}
module IceGrid {
interface FileIterator {
    bool read(int size, out Ice::StringSeq lines)
        throws FileNotAvailableException;
    void destroy();
};
};
{zcode}

A client may invoke the read operation as many times as necessary. The size argument specifies the maximum number of bytes that read can return; the client must not use a size that would cause the reply to exceed the client's configured maximum message size.

If this is the client's first call to read, the lines argument holds whatever text was available from the iterator's initial position, and the iterator is repositioned in preparation for the next call to read. The operation returns false to indicate that more text is available and true if all available text has been read.

Line termination characters are removed from the contents of lines. When displaying the text, you must be aware that the first and last elements of the sequence can be partial lines. For example, the last line of the sequence might be incomplete if the limit specified by size is reached. The next call to read returns the remainder of that line as the first element in the sequence.

As an example, the C++ code below displays the contents of a log file and waits for new text to become available:

{zcode:cpp}
IceGrid::FileIteratorPrx iter = ...;
while(true) {
    Ice::StringSeq lines;
    bool end = iter->read(10000, lines);
    if (!lines.empty()) {
        // The first line might be a continuation from
        // the previous call to read.
        cout << lines[0];
        for (Ice::StringSeq::const_iterator p = ++lines.begin(); p != lines.end(); ++p)
            cout << endl << *p << flush;
    }
    if (end)
        sleep(1);
}
{zcode}

Notice that the loop includes a delay in case read returns true, which prevents the client from entering a busy loop when no data is currently available.

The client should call destroy when the iterator object is no longer required. At the time the client's session terminates, IceGrid reclaims any iterators that were not explicitly destroyed.

If the client waits for new data, it must invoke keepAlive on the administrative session to prevent it from expiring.

Dynamic Monitoring in IceGrid

IceGrid allows an application to monitor relevant state changes by registering callback objects. (The IceGrid GUI tool uses these callback interfaces for its implementation.) The callback interfaces are useful to, for example, automatically generate an email notification when a node goes down or some other state change of interest occurs.

Observer Interfaces

IceGrid offers a callback interface for each major component of the IceGrid architecture:

{zcode:slice}
module IceGrid {
interface NodeObserver {
    void nodeInit(NodeDynamicInfoSeq nodes);
    void nodeUp(NodeDynamicInfo node);
    void nodeDown(string name);
    void updateServer(string node, ServerDynamicInfo updatedInfo);
    void updateAdapter(string node, AdapterDynamicInfo updatedInfo);
};

interface ApplicationObserver {
    void applicationInit(int serial, ApplicationInfoSeq applications);
    void applicationAdded(int serial, ApplicationInfo desc);
    void applicationRemoved(int serial, string name);
    void applicationUpdated(int serial, ApplicationUpdateInfo desc);
};

interface AdapterObserver {
    void adapterInit(AdapterInfoSeq adpts);
    void adapterAdded(AdapterInfo info);
    void adapterUpdated(AdapterInfo info);
    void adapterRemoved(string id);
};

interface ObjectObserver {
    void objectInit(ObjectInfoSeq objects);
    void objectAdded(ObjectInfo info);
    void objectUpdated(ObjectInfo info);
    void objectRemoved(Ice::Identity id);
};

interface RegistryObserver {
    void registryInit(RegistryInfoSeq registries);
    void registryUp(RegistryInfo node);
    void registryDown(string name);
};
};
{zcode}

The next section describes how to install an observer.

Registering Observers

The AdminSession interface provides two operations for registering your observers:

{zcode:slice}
module IceGrid {
    interface AdminSession extends Glacier2::Session {
        idempotent void keepAlive();

        idempotent void setObservers(
                RegistryObserver* registryObs,
                NodeObserver* nodeObs,
                ApplicationObserver* appObs,
                AdapterObserver* adptObs,
                ObjectObserver* objObs)
            throws ObserverAlreadyRegisteredException;

        idempotent void setObserversByIdentity(
                Ice::Identity registryObs,
                Ice::Identity nodeObs,
                Ice::Identity appObs,
                Ice::Identity adptObs,
                Ice::Identity objObs)
            throws ObserverAlreadyRegisteredException;

        // ...
    };
};
{zcode}

You should invoke setObservers and supply proxies when it is possible for the registry to establish a separate connection to the client to deliver its callbacks. If network restrictions such as firewalls prevent such a connection, you should use the setObserversByIdentity operation, which creates a bidirectional connection instead.

You can pass a null proxy for any parameter to setObservers, or an empty identity for any parameter to setObserversByIdentity, if you want to use only some of the observers. In addition, passing a null proxy or an empty identity for an observer cancels a previous registration of that observer. The operations raise ObserverAlreadyRegisteredException if you pass a proxy or identity that was registered in a previous call.

Once the observers are registered, operations corresponding to state changes will be invoked on the observers. (See the Slice API Reference for details on the data passed to the observers. You can also look at the source code for the IceGrid GUI implementation in the Ice for Java distribution to see how observers are used by the GUI.)

Finally, remember to invoke keepAlive periodically on the administrative session to prevent it from expiring.

See Also