Skip to main content

Client library for Apache Ambari.

Project description

Python bindings to the Apache Ambari API
==================================================

All syntax examples will assume you are using the client against the test host
created by following the Ambari Quick Start Guide. I've set up a 5 node
cluster which has hostnames `c6401-c6405.ambari.apache.org`. The examples will
translate to other clusters, just the hostnames and number of hosts might
change, obviously.


Basic API bindings design
----------

The starting point for users is the Ambari class in `ambariclient.client`:

```python
>>> from ambariclient.client import Ambari
>>> client = Ambari('c6401.ambari.apache.org', port=8080, username='admin', password='admin')
```


Ambari Shell
-----------

The Ambari Shell will automatically set up the ambari client for you and let
you play around with the API to learn the syntax. To load it run the
`ambari-shell` executable. It should have been put in a system path for you
automatically at install time, but if you're just playing with the source code,
you can call `bin/ambari-shell` directly. It is based on the IPython shell, so
for more comprehensive documentation of all the capabilities provided by that,
refer to
[the IPython documentation](http://ipython.org/ipython-doc/stable/interactive/index.html).
Note that the shell functionality requires that you install IPython:

```bash
$ pip install IPython
```

Here is an

```
$ ambari-shell

Python 2.7.6 (default, Sep 9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
Logging level set to 50
Python 2.7.6 (default, Sep 9 2014, 15:04:36)
Type "copyright", "credits" or "license" for more information.

IPython 3.1.0 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.


Ambari client available as 'ambari'
- Ambari Server is http://c6401.ambari.apache.org:8080
- Ambari Version is 2.0.0

- log(new_level) will reset the logger level
- ambari_ref() will show you all available client method chains

In [1]:
```

You can then just reference the `ambari` variable as the client. The client is configurable via
command-line options and/or a config file. The defaults are those for the Ambari Quick Start
setup. Username and password are defaulted to `admin`, and the host is defaulted to
`http://c6401.ambari.apache.org:8080`

You can override them by creating a JSON configuration file in `$HOME/.ambari`:

```
{
"host": "my.ambari.server.com",
"port": 80,
"username": "my-username",
"password": "my-password"
}
```

You can also pass in any of those options on the CLI:

```bash
$ ambari-shell --host http://my.ambari.server.com --username my-username --password my-password
```

Or you can specify some in the config file and then override some using the CLI switches.

You can also override the logger level by passing it in or putting it in the config file:

```bash
$ ambari-shell --logger INFO
```

To get more information about the switches, run `ambari-shell --help`


Basic Examples
-----------

All of the various resources are broken down into what are known as collections.
Each collection contains one or more models, which represent resources in the
Ambari API. For example, to get the usernames of all users in the system:

```python
>>> for user in ambari.users:
... user.user_name
...
u'admin'
```

You can get a specific model from a collection if you have the primary identifier
for that model. So, for a user, the `user_name` is the primary identifier. To get
the 'admin' user:

```python
>>> user = ambari.users('admin')
>>> user.user_name
u'admin'
```

Each model can then, in turn, contain collections of models that are subordinate
to it. In the case of users, there is a relationship to what are called
privileges:

```python
>>> for privilege in ambari.users('admin').privileges:
... privilege.permission_name
...
u'AMBARI.ADMIN'
```

The API tries to mimic a promises-style API (also called futures). It will
only load data when it is required to proceed. So, for example, in the
previous example, we had to issue a GET request when we called
`privilege.permission_name` because `permission_name` was not known until that
point. However, if we simply needed to get the `permission_id` instead, that
information is returned by the `GET` request on the user object. I'm including
the relevant debug log statements to show the difference:

```python
>>> for privilege in ambari.users('admin').privileges:
... privilege.permission_name
...
DEBUG:requests.packages.urllib3.connectionpool:"GET /api/v1/users/admin HTTP/1.1" 200 416
DEBUG:ambariclient.client:Response: {
"href" : "http://c6401.ambari.apache.org:8080/api/v1/users/admin",
"Users" : {
"active" : true,
"admin" : true,
"groups" : [ ],
"ldap_user" : false,
"user_name" : "admin"
},
"privileges" : [
{
"href" : "http://c6401.ambari.apache.org:8080/api/v1/users/admin/privileges/1",
"PrivilegeInfo" : {
"privilege_id" : 1,
"user_name" : "admin"
}
}
]
}
DEBUG:requests.packages.urllib3.connectionpool:"GET /api/v1/users/admin/privileges/1 HTTP/1.1" 200 287
DEBUG:ambariclient.client:Response: {
"href" : "http://c6401.ambari.apache.org:8080/api/v1/users/admin/privileges/1",
"PrivilegeInfo" : {
"permission_name" : "AMBARI.ADMIN",
"principal_name" : "admin",
"principal_type" : "USER",
"privilege_id" : 1,
"type" : "AMBARI",
"user_name" : "admin"
}
}
u'AMBARI.ADMIN'
```

Notice that two `GET` requests were sent to load the appropriate data. The
first is sent as soon as you access the `privileges` attribute on
`ambari.users('admin')`. Most people would expect it to be sent on
`ambari.users('admin')` but at that point you aren't attempting to access any
data unknown to the object, so no API call is sent. Accessing a relationship
requires the object to be populated as the relationship data is often returned
in the original request, saving a separate API call later. Now, back to the
example:

```python
>>> for privilege in ambari.users('admin').privileges:
... privilege.privilege_id
...
DEBUG:requests.packages.urllib3.connectionpool:"GET /api/v1/users/admin HTTP/1.1" 200 416
DEBUG:ambariclient.client:Response: {
"href" : "http://c6401.ambari.apache.org:8080/api/v1/users/admin",
"Users" : {
"active" : true,
"admin" : true,
"groups" : [ ],
"ldap_user" : false,
"user_name" : "admin"
},
"privileges" : [
{
"href" : "http://c6401.ambari.apache.org:8080/api/v1/users/admin/privileges/1",
"PrivilegeInfo" : {
"privilege_id" : 1,
"user_name" : "admin"
}
}
]
}
1
```

In this case, only the GET request for the user object was required, since it
provided the needed `privilege_id` information.

The last concept you need to be aware of is the `wait()` method that exists on
nearly all objects in the system, both collections and models. Calling
`wait()` will force a sync operation to occur. What this means in practical
terms is:

- If the object is backed by a URL, it will be loaded with fresh data from the
server
- If the object had a method called on it that generated a `Request` object on
the server (i.e. a long-running asynchronous process), it will poll that
request until it is completed

The basic idea is to wait until the object is in a 'ready' state, so that it can
be further acted upon. The method is designed for method-chaining, so you can do
fun things like:

```python
>>> host.components.install().wait().start().wait()
```

It's most-commonly useful after a `create()` call:

```python
>>> cluster = ambari.clusters.create(name, blueprint=bp_name,
host_groups=host_groups,
default_password=pwd
).wait(timeout=1800, interval=30)
```

You can override the default timeout and interval settings if you have a good
idea of how long you expect the process to take. Both values are in seconds.
The default is to poll every 15s for an hour, which is often excessive.


Host bootstrapping
-----------

For testing things out, the bootstrap API is very useful. To start a bootstrap
process using this library, it's pretty simple:

```python
>>> hosts = ["c6401.ambari.apache.org", "c6402.ambari.apache.org", "c6403.ambari.apache.org", "c6404.ambari.apache.org"]
>>> ssh_key = ''
>>> with open("/path/to/insecure_private_key") as f:
... ssh_key = f.read()
...
>>> bootstrap = ambari.bootstrap.create(hosts=hosts, sshKey=ssh_key, user='vagrant')
>>> bootstrap.wait()
```

There is no way to retrieve a list of bootstrap operations, so don't lose track
of the one you started. This is a server-side API restriction:

```python
>>> ambari.bootstrap.wait()
ambariclient.exceptions.MethodNotAllowed: HTTP request failed for GET http://c6401.ambari.apache.org:8080/api/v1/bootstrap: Method Not Allowed 405: {
"status": 405,
"message": "Method Not Allowed"
}
```


API Hierarchy
-----------

For reference, this is the currently-supported hierarchy of collections and models
available for you to use. This list can be regenerated by calling reference()
in the ambari-shell. We'll try to keep it up-to-date:

- `ambari.actions`
- `ambari.actions(action_name)`
- `ambari.alert_targets`
- `ambari.alert_targets(id)`
- `ambari.blueprints`
- `ambari.blueprints(blueprint_name)`
- `ambari.blueprints(blueprint_name).host_groups`
- `ambari.blueprints(blueprint_name).host_groups(name)`
- `ambari.bootstrap`
- `ambari.bootstrap(requestId)`
- `ambari.clusters`
- `ambari.clusters(cluster_name)`
- `ambari.clusters(cluster_name).alert_definitions`
- `ambari.clusters(cluster_name).alert_definitions(id)`
- `ambari.clusters(cluster_name).alert_groups`
- `ambari.clusters(cluster_name).alert_groups(id)`
- `ambari.clusters(cluster_name).alert_history`
- `ambari.clusters(cluster_name).alert_history(id)`
- `ambari.clusters(cluster_name).alert_notices`
- `ambari.clusters(cluster_name).alert_notices(id)`
- `ambari.clusters(cluster_name).alerts`
- `ambari.clusters(cluster_name).alerts(id)`
- `ambari.clusters(cluster_name).configurations`
- `ambari.clusters(cluster_name).configurations(type)`
- `ambari.clusters(cluster_name).host_components`
- `ambari.clusters(cluster_name).host_components(component_name)`
- `ambari.clusters(cluster_name).hosts`
- `ambari.clusters(cluster_name).hosts(host_name)`
- `ambari.clusters(cluster_name).hosts(host_name).alert_history`
- `ambari.clusters(cluster_name).hosts(host_name).alert_history(id)`
- `ambari.clusters(cluster_name).hosts(host_name).alerts`
- `ambari.clusters(cluster_name).hosts(host_name).alerts(id)`
- `ambari.clusters(cluster_name).hosts(host_name).components`
- `ambari.clusters(cluster_name).hosts(host_name).components(component_name)`
- `ambari.clusters(cluster_name).privileges`
- `ambari.clusters(cluster_name).privileges(privilege_id)`
- `ambari.clusters(cluster_name).requests`
- `ambari.clusters(cluster_name).requests(id)`
- `ambari.clusters(cluster_name).requests(id).tasks`
- `ambari.clusters(cluster_name).requests(id).tasks(id)`
- `ambari.clusters(cluster_name).services`
- `ambari.clusters(cluster_name).services(service_name)`
- `ambari.clusters(cluster_name).services(service_name).alert_history`
- `ambari.clusters(cluster_name).services(service_name).alert_history(id)`
- `ambari.clusters(cluster_name).services(service_name).alerts`
- `ambari.clusters(cluster_name).services(service_name).alerts(id)`
- `ambari.clusters(cluster_name).services(service_name).components`
- `ambari.clusters(cluster_name).services(service_name).components(component_name)`
- `ambari.clusters(cluster_name).services(service_name).components(component_name).host_components`
- `ambari.clusters(cluster_name).services(service_name).components(component_name).host_components(component_name)`
- `ambari.clusters(cluster_name).services(service_name).components(component_name).metrics`
- `ambari.clusters(cluster_name).services(service_name).components(component_name).metrics(name)`
- `ambari.clusters(cluster_name).upgrades`
- `ambari.clusters(cluster_name).upgrades(request_id)`
- `ambari.clusters(cluster_name).upgrades(request_id).groups`
- `ambari.clusters(cluster_name).upgrades(request_id).groups(group_id)`
- `ambari.clusters(cluster_name).upgrades(request_id).groups(group_id).items`
- `ambari.clusters(cluster_name).upgrades(request_id).groups(group_id).items(stage_id)`
- `ambari.clusters(cluster_name).upgrades(request_id).groups(group_id).items(stage_id).tasks`
- `ambari.clusters(cluster_name).upgrades(request_id).groups(group_id).items(stage_id).tasks(id)`
- `ambari.groups`
- `ambari.groups(group_name)`
- `ambari.groups(group_name).members`
- `ambari.groups(group_name).members(user_name)`
- `ambari.hosts`
- `ambari.hosts(host_name)`
- `ambari.services`
- `ambari.services(service_name)`
- `ambari.services(service_name).components`
- `ambari.services(service_name).components(component_name)`
- `ambari.services(service_name).components(component_name).host_components`
- `ambari.stacks`
- `ambari.stacks(stack_name)`
- `ambari.stacks(stack_name).versions`
- `ambari.stacks(stack_name).versions(stack_version)`
- `ambari.stacks(stack_name).versions(stack_version).operating_systems`
- `ambari.stacks(stack_name).versions(stack_version).operating_systems(os_type)`
- `ambari.stacks(stack_name).versions(stack_version).operating_systems(os_type).repositories`
- `ambari.stacks(stack_name).versions(stack_version).operating_systems(os_type).repositories(repo_id)`
- `ambari.stacks(stack_name).versions(stack_version).services`
- `ambari.stacks(stack_name).versions(stack_version).services(service_name)`
- `ambari.stacks(stack_name).versions(stack_version).services(service_name).components`
- `ambari.stacks(stack_name).versions(stack_version).services(service_name).components(component_name)`
- `ambari.stacks(stack_name).versions(stack_version).services(service_name).configurations`
- `ambari.users`
- `ambari.users(user_name)`
- `ambari.users(user_name).privileges`
- `ambari.users(user_name).privileges(privilege_id)`
- `ambari.views`
- `ambari.views(view_name)`
- `ambari.views(view_name).versions`
- `ambari.views(view_name).versions(version)`
- `ambari.views(view_name).versions(version).instances`
- `ambari.views(view_name).versions(version).instances(instance_name)`
- `ambari.views(view_name).versions(version).permissions`
- `ambari.views(view_name).versions(version).permissions(permission_id)`


Testing
-------

Since doing good unit tests for this library would require mocking pretty much
the entire Ambari API, we didn't see a lot of benefit in that. There are some
basic unit tests that will be expanded upon over time, but most testing is
currently done manually.

```bash
$ tox -e py27
$ tox -e py33
$ tox -e
```

Ambari Versions
---------------

The goal of the client is to work with multiple versions of Ambari by smoothing
out the differences for the user. The client can automatically detect the
version of Ambari running on the server:

```python
>>> ambari.version
('1', '7', '0')
```

This client has only been tested against Ambari 1.7+. If you need
support for older versions, you might need to submit patches for anything
that was changed after that time. Supporting anything prior to 1.6 is
problematic as the basic method of creating clusters changed to require
blueprints in that release, but if you have a good idea on how to make it
seamless, then we can try to integrate the solution.


Python Versions
---------------

The goal is to support Python 2.7+ and 3.3+. I'm not opposed to adding 2.6
support, but someone else will need to do the work.


Command-Line Interface
----------------------

A CLI was originally planned, but never implemented. Some methods that require
a large JSON body are not amenable to a CLI, but we could easily build one for
other methods if people want one. We replaced the idea with the Ambari Shell for
now, but are open to revisiting this if demand is present.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python-ambariclient-0.5.10.tar.gz (34.1 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page