Configuring osh

If osh is used to access databases or clusters, then connection information is required. This information is kept in a configuration file. The configuration file can also be used to define variable and function definitions for use in osh commands.

Location of the osh configuration file

The configuration file is located in one of three places, searched in this order: If none of these files exist, then osh will not be able to access any database or cluster.

If the osh configuration file is going to be used for database access, then it will contain a plaintext database password. It is therefore good practice to restrict access to this file, e.g. set permissions to 600.

Structure of the configuration file

The .oshrc file contains Python code. The API for specifying a configuration is contained in the module osh.config, so .oshrc always begins with this line:
    from osh.config import *
Database and cluster configuration is done using field and slice notation. For example, to specify that access to the cluster named fred will be done as root:
    osh.remote.fred.user = 'root'
Slice notation is also supported, e.g.
    osh.remote['fred'].user = 'root'
This is useful in case a database or cluster name is known dynamically, or is not a legal Python identifier.

Configuring database access

Database access from osh is done using the sql command, e.g.
    osh sql mydb "select count(*) from request" $
mydb is the name of a database profile, specified in .oshrc, e.g.
    osh.sql.mydb.dbtype = 'postgres'
    osh.sql.mydb.host = 'localhost'
    osh.sql.mydb.db = 'sales_db'
    osh.sql.mydb.user = 'fred'
    osh.sql.mydb.password = 'l3tme1n'
A default database profile can be specified, e.g.
    osh.sql = 'mydb'
If this is done, then the sql command can omit the database profile name, e.g.
    osh sql "select count(*) from request" $

Cluster configuration

This command executes a query on each node of cluster fred:
    osh @fred [ sql familydb "select name, age from person" ] $
Each node of fred's cluster must configure a database profile named familydb in its .oshrc file.

On the local node (the one on which the above command is entered), the cluster fred must be configured, e.g.

    osh.remote.fred.user = 'root'
    osh.remote.fred.hosts = ['192.168.100.101',
                             '192.168.100.102',
                             '192.168.100.103']
This says that the cluster fred is accessed by root and that the cluster consists of nodes 192.168.100.101, 192.168.100.102 and 192.168.100.103.

When a remote command is run on a cluster, each row of output identifies the node that generated the output, e.g.

    zack$ osh @fred [ sql familydb "select name, age from person" ] $
    ('192.168.111.101', 'hannah', 14)
    ('192.168.111.101', 'julia', 9)
    ('192.168.111.102', 'alexander', 15)
    ('192.168.111.102', 'nathan', 13)
    ('192.168.111.102', 'zoe', 11)
    ('192.168.111.103', 'danica', 1)
The hosts part of a cluster specification can also be done using a dict, in which the key is a name for the node, and the value is the IP address, e.g.
    osh.remote.fred.user = 'root'
    osh.remote.fred.hosts = {'101': '192.168.100.101',
                             '102': '192.168.100.102',
                             '103': '192.168.100.103'}
The node names are used in command output in place of IP addresses, e.g.
    zack$ osh @fred [ sql familydb "select name, age from person" ] $
    ('101', 'hannah', 14)
    ('101', 'julia', 9)
    ('102', 'alexander', 15)
    ('102', 'nathan', 13)
    ('102', 'zoe', 11)
    ('103', 'danica', 1)
These examples assume that each node of cluster fred has a .oshrc file that configures a database profile named familydb. It is also possible to configure database access even when each node specifies a different database profile, e.g.
    osh.remote.fred.user = 'root'
    osh.remote.fred.hosts = {'101': {'host': '192.168.100.101', 'db_profile': 'db1'},
                             '102': {'host': '192.168.100.102', 'db_profile': 'db2'},
                             '103': {'host': '192.168.100.103', 'db_profile': 'db3'}}
With this configuration osh @fred [ sql "select ..." ] ... accesses db1 on node 101, db2 on node 102, and db3 on node 103.

The complete rules for selecting a database profile are as follows:

Arbitrary Python code in .oshrc

.oshrc contains Python code, and any symbols defined in it are available to osh commands. For example, this code prints the squares and cubes of the first ten integers:
    zack$ osh gen 10 ^ f 'x: (x**2, x**3)' $
The same computation can be done by writing a function to compute squares and cubes:
    def square_and_cube(x):
        return x**2, x**3
and putting this function definition in .oshrc. Then, the osh command above can be rewritten as:
    zack$ osh gen 10 ^ f 'x: square_and_cube(x)' $

Locating user-defined osh commands

It is easy to write your own osh commands (although the procedure is currently not documented). In order for such commands to be located by the osh interpreter, a search path needs to be specified in .oshrc. For example, suppose you have written a command xyz and placed its implementation, xyz.py, in /usr/local/foobar. Then you must add this to .oshrc:
    osh.path = ['/usr/local/foobar']
or the osh interpreter will not find xyz when used in an osh command sequence.

The value of osh.path is a list so that custom commands can be placed in any number of directories.