Clusto API Server¶
About¶
The Clusto API server intends to replace the direct database approach used by most clusto commands and scripts right now. The goal is to make this API robust enough to permit all operations usually handled via clusto commands or the clusto shell but using a REST/HTTP interface.
Version 0.5.2 (2015-08-21)¶
New feature/bugfix release
Features¶
- Adding developer documentation (Jorge Gallegos)
Bugfixes¶
- Fixed the OPTIONS dummy routers (Jorge Gallegos)
User documentation¶
Main server components¶
clustoapi.server: Server main module¶
Overview¶
The Clusto API Server will work as an alternative to the direct database access traditional clusto commands and libraries use.
Sometimes your database has restricted access or you want to expose your clusto information across facilities but don’t want to risk opening up your database port to the outside world or don’t have the required know-how to do it in a secure manner.
Exposing an HTTP(S) endpoint is a more common problem and as such there are several well-understood solutions. Scalability and security are also points you have to consider.
The Clusto API Server should thus have the following required features:
- Complete object manipulation: create, delete, insert, and remove objects, besides displaying them.
- Complete object attribute manipulation: add, delete, update, query, and displaying attributes
- Resource manipulation: allocate and deallocate objects from resources
- Querying
Custom Headers¶
The Clusto API Server comes with the ability to pass certain headers to multiple operations.
Clusto-Mode: | Determines if an object is compact or expanded .
Compaction of objects helps speed up response time for multiple object
lookups. expanded is the default mode if the function returns only one
object, and is compact by default for all listing functions. |
---|---|
Clusto-Per-Page: | |
Number of entities to return when pagination is requested.
Defaults to 50 . |
|
Clusto-Page: | Requests the current page in a list of entities, delimited by
Clusto-Per-Page . |
Clusto-Pages: | Response only. This header returns the total number of pages to the requester. |
Clusto-Minify: | If set to True (not case sensitive), clusto will not
give a response that has been pretty-printed. |
Configurable Response Headers¶
The Clusto API Server comes with the ability to define and configure static
response headers within the clusto.conf
.
Example: | To enable CORS, a line in the clusto.conf would read:
response_headers = Access-Control-Allow-Origin:* |
---|
API Docs¶
-
clustoapi.server.
build_docs
(module='clustoapi.server')[source]¶ This will build documentation for the given module and all its methods. If python-rest is available, it will attempt to parse it as a restructured text document. You can get to the docs by going to the __doc__ endpoint on each mounted application, the main __doc__ endpoint, or on the main endpoint:
$ ${get} ${server_url}/__doc__ <?xml version="1.0" encoding="utf-8" ?> ... HTTP: 200 Content-type: text/html; charset=UTF-8
If you pass the
Accept
headers and specifytext/plain
, you should get the plain text version back$ ${get} -H 'Accept: text/plain' ${server_url}/__doc__ ... HTTP: 200 Content-type: text/plain $ diff -q <( curl -s -H 'Accept: text/plain' ${server_url}/__doc__ ) <( curl -s -H 'Accept: text/plain' ${server_url}/ ) && echo 'equal' || echo 'diff' equal
In the test config, CORS is configured to be returned with every response header.
Or:
If you try to get a non-configured header, it shouldn’t be in the output
-
clustoapi.server.
favicon
()[source]¶ Send an HTTP code to clients so they stop asking for favicon. Example:
$ ${get} -o /dev/null ${server_url}/favicon.ico HTTP: 410 Content-type: text/html; charset=UTF-8
-
clustoapi.server.
get_by_attr
()[source]¶ One of the main
clusto
operations. Parameters:- Required: the
key
parameter - Optional: the
subkey
parameter - Optional: the
value
parameter
Examples:
$ ${get} ${server_url}/by-attr "Provide a key to use get_by_attr" HTTP: 412 Content-type: application/json $ ${get} -d 'key=nonkey' ${server_url}/by-attr [] HTTP: 200 Content-type: application/json $ ${get} -d 'key=key1' -d 'subkey=subkey1' -d 'value=value1' ${server_url}/by-attr [ "/basicserver/testserver1" ] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Mode: expanded' -d 'key=key1' ${server_url}/by-attr [ { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey1", "value": "value1" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver1", "parents": [ "/pool/singlepool", "/pool/multipool" ], "type": "server" }, { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey2", "value": "value2" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver2", "parents": [ "/pool/multipool" ], "type": "server" } ] HTTP: 200 Content-type: application/json
- Required: the
-
clustoapi.server.
get_by_name
(name)[source]¶ One of the main
clusto
operations. Parameters:- Required path parameter:
name
- The name you’re looking for - Optional:
driver
- If provided, a driver check will be added to ensure the resulting object is the type you’re expecting
Note
This function returns expanded objects by default in order to reduce the amount of required custom headers. Therefore, the header is not required to receive expanded objects.
Examples:
$ ${get} ${server_url}/by-name/nonserver "Object "nonserver" not found (nonserver does not exist.)" HTTP: 404 Content-type: application/json $ ${get} -H 'Clusto-Mode: compact' ${server_url}/by-name/testserver1 "/basicserver/testserver1" HTTP: 200 Content-type: application/json $ ${get} ${server_url}/by-name/testserver1 { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey1", "value": "value1" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver1", "parents": [ "/pool/singlepool", "/pool/multipool" ], "type": "server" } HTTP: 200 Content-type: application/json $ ${get} -d 'driver=pool' ${server_url}/by-name/testserver1 "The driver for object "testserver1" is not "pool"" HTTP: 409 Content-type: application/json $ ${get} -d 'driver=nondriver' ${server_url}/by-name/testserver1 "The driver "nondriver" is not a valid driver" HTTP: 412 Content-type: application/json
- Required path parameter:
-
clustoapi.server.
get_by_names
()[source]¶ One of the main
clusto
operations. Parameters:- Required parameter: At least one
name
parameter
Returns
HTTP: 404
when all entites requested do not exist andHTTP: 206
when a percent of entities requested do not exist.Examples:
$ ${get} ${server_url}/by-names "Provide at least one name to get data from" HTTP: 412 Content-type: application/json $ ${get} -d 'name=nonserver' ${server_url}/by-names [ null ] HTTP: 404 Content-type: application/json $ ${get} -d 'name=testserver1' -d 'name=nonserver' ${server_url}/by-names [ "/basicserver/testserver1", null ] HTTP: 206 Content-type: application/json $ ${get} -H 'Clusto-Mode: expanded' -d 'name=testserver1' -d 'name=testserver2' ${server_url}/by-names [ { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey1", "value": "value1" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver1", "parents": [ "/pool/singlepool", "/pool/multipool" ], "type": "server" }, { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey2", "value": "value2" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver2", "parents": [ "/pool/multipool" ], "type": "server" } ] HTTP: 200 Content-type: application/json $ ${get} -d 'name=nonserver1' -d 'name=nonserver2' ${server_url}/by-names [ null, null ] HTTP: 404 Content-type: application/json
- Required parameter: At least one
-
clustoapi.server.
get_driverlist
()[source]¶ Returns clusto.driverlist, a list of drivers and their qualified class paths.
Examples:
$ ${get} ${server_url}/driverlist { "basicappliance": "clusto.drivers.devices.appliance.basicappliance.BasicAppliance", "basiccage": "clusto.drivers.locations.datacenters.basiccage.BasicCage", "basicconsoleserver": "clusto.drivers.devices.consoleservers.basicconsoleserver.BasicConsoleServer", "basicdatacenter": "clusto.drivers.locations.datacenters.basicdatacenter.BasicDatacenter", "basicnetworkswitch": "clusto.drivers.devices.networkswitches.basicnetworkswitch.BasicNetworkSwitch", "basicpowerstrip": "clusto.drivers.devices.powerstrips.basicpowerstrip.BasicPowerStrip", "basicrack": "clusto.drivers.locations.racks.basicrack.BasicRack", "basicserver": "clusto.drivers.devices.servers.basicserver.BasicServer", "basicvirtualserver": "clusto.drivers.devices.servers.basicserver.BasicVirtualServer", "basiczone": "clusto.drivers.locations.zones.basiczone.BasicZone", "clustometa": "clusto.drivers.base.clustometa.ClustoMeta", "device": "clusto.drivers.base.device.Device", "entity": "clusto.drivers.base.driver.Driver", "exclusive_pool": "clusto.drivers.categories.pool.ExclusivePool", "ipmanager": "clusto.drivers.resourcemanagers.ipmanager.IPManager", "location": "clusto.drivers.base.location.Location", "pool": "clusto.drivers.categories.pool.Pool", "resourcemanager": "clusto.drivers.base.resourcemanager.ResourceManager", "simpleentitynamemanager": "clusto.drivers.resourcemanagers.simplenamemanager.SimpleEntityNameManager", "simplenamemanager": "clusto.drivers.resourcemanagers.simplenamemanager.SimpleNameManager", "unique_pool": "clusto.drivers.categories.pool.UniquePool" } HTTP: 200 Content-type: application/json
-
clustoapi.server.
get_from_pools
()[source]¶ One of the main
clusto
operations. Parameters:- Required: at least one
pool
parameter - Optional: one or more
driver
parameter to filter out results - Optional: one or more
type
parameter to filter out results - Optional: a boolean
children
parameter to search for children recursively (True by default)
Examples:
$ ${get} ${server_url}/from-pools "Provide at least one pool to get data from" HTTP: 412 Content-type: application/json $ ${get} -H 'Clusto-Page: notanint' -d 'pool=emptypool' ${server_url}/from-pools "invalid literal for int() with base 10: 'notanint'" HTTP: 400 Content-type: application/json $ ${get} -d 'pool=emptypool' ${server_url}/from-pools [] HTTP: 200 Content-type: application/json $ ${get} -d 'pool=singlepool' -d 'pool=multipool' ${server_url}/from-pools [ "/basicserver/testserver1" ] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Mode: expanded' -d 'pool=multipool' ${server_url}/from-pools [ { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey1", "value": "value1" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver1", "parents": [ "/pool/singlepool", "/pool/multipool" ], "type": "server" }, { "attrs": [ { "datatype": "string", "key": "key1", "number": null, "subkey": "subkey2", "value": "value2" } ], "contents": [], "driver": "basicserver", "ips": [], "name": "testserver2", "parents": [ "/pool/multipool" ], "type": "server" } ] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Page: 1' -H 'Clusto-Per-Page: 1' -d 'pool=multipool' ${server_url}/from-pools [ "/basicserver/testserver1" ] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Page: 1' -H 'Clusto-Per-Page: 100' -d 'pool=multipool' ${server_url}/from-pools [ "/basicserver/testserver1", "/basicserver/testserver2" ] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Page: 100' -H 'Clusto-Per-Page: 100' -d 'pool=multipool' ${server_url}/from-pools [] HTTP: 200 Content-type: application/json $ ${get} -H 'Clusto-Minify: True' -d 'pool=multipool' ${server_url}/from-pools ["/basicserver/testserver1", "/basicserver/testserver2"] HTTP: 200 Content-type: application/json
- Required: at least one
-
clustoapi.server.
get_typelist
()[source]¶ Returns clusto.typelist, a list of the most recent drivers to register with that type.
Examples:
$ ${get} ${server_url}/typelist { "appliance": "clusto.drivers.devices.appliance.basicappliance.BasicAppliance", "cage": "clusto.drivers.locations.datacenters.basiccage.BasicCage", "clustometa": "clusto.drivers.base.clustometa.ClustoMeta", "consoleserver": "clusto.drivers.devices.consoleservers.basicconsoleserver.BasicConsoleServer", "datacenter": "clusto.drivers.locations.datacenters.basicdatacenter.BasicDatacenter", "generic": "clusto.drivers.base.device.Device", "location": "clusto.drivers.base.location.Location", "networkswitch": "clusto.drivers.devices.networkswitches.basicnetworkswitch.BasicNetworkSwitch", "pool": "clusto.drivers.categories.pool.UniquePool", "powerstrip": "clusto.drivers.devices.powerstrips.basicpowerstrip.BasicPowerStrip", "rack": "clusto.drivers.locations.racks.basicrack.BasicRack", "resourcemanager": "clusto.drivers.resourcemanagers.ipmanager.IPManager", "server": "clusto.drivers.devices.servers.basicserver.BasicVirtualServer", "zone": "clusto.drivers.locations.zones.basiczone.BasicZone" } HTTP: 200 Content-type: application/json
-
clustoapi.server.
meta
()[source]¶ This call just returns a mapping of all currently installed applications.
$ ${get} ${server_url}/__meta__ ... HTTP: 200 Content-type: application/json
-
clustoapi.server.
options
(**kwargs)[source]¶ Defined from w3.org:
“The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.”The clusto-apiserver team plans to roll this out to individual resources once it has been used a proper amount, but for now we will return OPTIONS with the minimum amount of required headers (and an empty content) no matter what resource is requested.
$ ${head} -X OPTIONS ${server_url}/ HTTP/1.0 204 No Content ... Content-Length: 0 $ ${head} -X OPTIONS ${server_url}/return/headers/no/matter/where HTTP/1.0 204 No Content ... Content-Length: 0
The same applies to any mounted application:
$ ${head} -X OPTIONS ${server_url}/entity/ HTTP/1.0 204 No Content ... Content-Length: 0 $ ${head} -X OPTIONS ${server_url}/attribute/who/knows/where HTTP/1.0 204 No Content ... Content-Length: 0
-
clustoapi.server.
version
()[source]¶ This shows the current version running, example
$ ${get} ${server_url}/__version__ "${server_version}" HTTP: 200 Content-type: application/json
If you make a HEAD request to the / endpoint, the response is also the version string, as that’s less heavy to build than the regular / page:
$ ${head} ${server_url}/ HTTP/1.0 200 OK ...
clustoapi.util: Miscellaneous util module¶
-
clustoapi.util.
dumps
(obj, code=200, headers={})[source]¶ Dumps a given object as a JSON string in an HTTP Response object. Will circumvent pretty-printing if Clusto-Minify header is True.
-
clustoapi.util.
get
(name, driver=None)[source]¶ Tries to fetch a clusto object from a given name, optionally validating the driver given. Returns:
- HTTP Error 404 if the object could not be found
- HTTP Error 409 if the object does not match the expected driver
- Clusto object otherwise
-
clustoapi.util.
page
(ents, current=1, per=50)[source]¶ Takes a list of entities and drops all from a list but the current page. Returns a tuple that has the entities and also a page total so it may be returned to the client.
-
clustoapi.util.
show
(obj, mode='')[source]¶ Will return the expanded or compact representation of a given object
-
clustoapi.util.
typecast
(value, datatype, mask='%Y-%m-%dT%H:%M:%S.%f')[source]¶ Takes a string and a valid clusto datatype and attempts to cast the value to the specified datatype. Will error out if a ValueError is incurred or a relation does not exist. Will aslo take a strptime format as
mask
because typcasting datetimes is hard.
Core applications¶
clustoapi.apps.attribute: Attribute Application¶
The attribute
application handles all attribute specific operations like
querying, adding, deleting and updating attributes.
-
clustoapi.apps.attribute.
add_attr
(name, **kwargs)[source]¶ Add an attribute to this object.
- Requires parameters
name
,key
, andvalue
- Optional parameters are
subkey`,` ``number
, anddatatype
- Additionally,
mask
can be provided for a datetime attribute. - These parameters can be either be passed with a querystring
- or a json body. If json is supplied, multiple attributes may be
- added at the same time.
Example:
$ ${post} -d 'name=addattrserver' ${server_url}/entity/basicserver [ "/basicserver/addattrserver" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'key=group' -d 'value=web' ${server_url}/attribute/addattrserver [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "web" } ] HTTP: 201 Content-type: application/json
Will:
- Create an entity called
addattrserver
- Add the attribute with
key=group
andvalue=web
to it
Example:
$ ${post} -d 'key=group' -d 'subkey=owner' -d 'value=web' ${server_url}/attribute/addattrserver [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "web" }, { "datatype": "string", "key": "group", "number": null, "subkey": "owner", "value": "web" } ] HTTP: 201 Content-type: application/json
Will add the attribute with key
group
and subkeyowner
and valuejoe
to the previously created entityaddattrserver
$ ${post} -d 'key=group' -d 'subkey=id' -d 'value=6' -d 'datatype=int' ${server_url}/attribute/addattrserver [ ... { "datatype": "int", "key": "group", "number": null, "subkey": "id", "value": 6 } ] HTTP: 201 Content-type: application/json
Will add the attribute with key
group
and subkeyid
and value1
with the correct datatype to the previously created entityaddattrserver
$ ${post} -d 'key=inception' -d 'value=/basicserver/addattrserver' -d 'datatype=relation' ${server_url}/attribute/addattrserver [ ... { "datatype": "relation", "key": "inception", "number": null, "subkey": null, "value": "/basicserver/addattrserver" } ] HTTP: 201 Content-type: application/json
Will add the attribute with key
inception
and itself as a relation using therelation
datatype.$ ${post} -d 'key=birthday' -d 'subkey=jtcunning' -d 'value=1991-07-09T14:46:51.321435' -d 'datatype=datetime' ${server_url}/attribute/addattrserver [ { "datatype": "datetime", "key": "birthday", "number": null, "subkey": "jtcunning", "value": "1991-07-09T14:46:51.321435" }, ... ] HTTP: 201 Content-type: application/json
Will add the attribute with key
birthday
and subkeyjtcunning
with thedatetime
python object as the datatype.$ ${post} -H 'Content-Type: application/json' -d '${sample_json_attrs}' ${server_url}/attribute/addattrserver [ ... { "datatype": "string", "key": "group", "number": null, "subkey": "admin", "value": "apache" }, { "datatype": "string", "key": "group", "number": null, "subkey": "member", "value": "webapp" }, ... ] HTTP: 201 Content-type: application/json
Will add two attributes in bulk by stating that the content type is
application/json
.- Requires parameters
-
clustoapi.apps.attribute.
attrs
(name, key=None, subkey=None, number=None)[source]¶ Query attributes from this object.
Example:
$ ${post} -d 'name=attrpool1' ${server_url}/entity/pool [ "/pool/attrpool1" ] HTTP: 201 Content-type: application/json
$ ${get} ${server_url}/attribute/attrpool1 [] HTTP: 200 Content-type: application/json
Will show all the attributes from the object
attrpool1
:$ ${get} -d 'driver=pool' ${server_url}/attribute/attrpool1 [] HTTP: 200 Content-type: application/json
Will show all the attributes from the object
attrpool1
if the driver forattrpool1
ispool
. In the same vein this code:$ ${get} -d 'driver=basicserver' ${server_url}/attribute/attrpool1 ... HTTP: 409 ...
Should fail, because the
attrpool1
object is of typepool
, notbasicserver
Example:
$ ${get} ${server_url}/attribute/attrpool1/owner [] HTTP: 200 Content-type: application/json
Will show the attributes for
server1
if their key isowner
.
-
clustoapi.apps.attribute.
del_attrs
(name, key, subkey=None, number=None)[source]¶ Deletes an attribute from this object
- Requires HTTP path
key
- Optional parameters are
subkey
,value
, andnumber
Examples:
$ ${post} -d 'name=deleteserver1' ${server_url}/entity/basicserver [ "/basicserver/deleteserver1" ] HTTP: 201 Content-type: application/json
$ ${put} -d 'value=joe' ${server_url}/attribute/deleteserver1/group/owner [ { "datatype": "string", "key": "group", "number": null, "subkey": "owner", "value": "joe" } ] HTTP: 200 Content-type: application/json
$ ${delete} ${server_url}/attribute/deleteserver1/group/owner [] HTTP: 200 Content-type: application/json
Will create a
basicserver
object calleddeleteserver1
, then it will add an attribute (the only attribute so far), then it will delete it.$ ${post} -d 'name=deleteserver2' ${server_url}/entity/basicserver [ "/basicserver/deleteserver2" ] HTTP: 201 Content-type: application/json
$ ${put} -d 'value=engineering' ${server_url}/attribute/deleteserver2/group [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "engineering" } ] HTTP: 200 Content-type: application/json
$ ${put} -d 'value=joe' ${server_url}/attribute/deleteserver2/group/owner [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "engineering" }, { "datatype": "string", "key": "group", "number": null, "subkey": "owner", "value": "joe" } ] HTTP: 200 Content-type: application/json
$ ${delete} ${server_url}/attribute/deleteserver2/group/owner [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "engineering" } ] HTTP: 200 Content-type: application/json
This example should add two attributes with the same key, but different subkey, then it will delete only the second value.
- Requires HTTP path
-
clustoapi.apps.attribute.
set_attr
(name, **kwargs)[source]¶ Sets an attribute from this object. If the attribute doesn’t exist it will be added, if the attribute already exists then it will be updated.
- Requires HTTP parameters
key
andvalue
- Optional parameters are
subkey`,` ``number
, anddatatype
- Additionally,
mask
can be provided for a datetime attribute.
Example:
$ ${post} -d 'name=setattrserver' ${server_url}/entity/basicserver [ "/basicserver/setattrserver" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'key=group' -d 'value=web' ${server_url}/attribute/setattrserver [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "web" } ] HTTP: 201 Content-type: application/json
$ ${put} -d 'value=db' ${server_url}/attribute/setattrserver/group [ { "datatype": "string", "key": "group", "number": null, "subkey": null, "value": "db" } ] HTTP: 200 Content-type: application/json
Will:
- Create the entity
setattrserver
- Add the attribute with
key=group
andvalue=web
- Update the attribute to
value=db
Example:
$ ${post} -d 'name=setattrserver2' ${server_url}/entity/basicserver [ "/basicserver/setattrserver2" ] HTTP: 201 Content-type: application/json
$ ${put} -d 'value=joe' ${server_url}/attribute/setattrserver2/group/owner [ { "datatype": "string", "key": "group", "number": null, "subkey": "owner", "value": "joe" } ] HTTP: 200 Content-type: application/json
$ ${put} -d 'value=bob' ${server_url}/attribute/setattrserver2/group/owner [ { "datatype": "string", "key": "group", "number": null, "subkey": "owner", "value": "bob" } ] HTTP: 200 Content-type: application/json
Will:
- Create a new object
setattrserver2
of typebasicserver
- Set the attribute with key
group
and subkeyowner
with valuejoe
to the objectsetattrserver1
. Since this is the only attribute so far, this operation works just likeadd_attr()
- Update the attribute we set above, now the
value
will readbob
- Requires HTTP parameters
clustoapi.apps.entity: Entity Application¶
The entity
application will hold all methods related to entity management
in clusto. That is: creation, querying, modification and overall entity
manipulation.
-
clustoapi.apps.entity.
action
(driver, name)[source]¶ Inserts/removes the given device from the request parameters into/from the object
Example:
$ ${post} -d 'name=pool1' ${server_url}/entity/pool [ "/pool/pool1" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'name=server1' ${server_url}/entity/basicserver [ "/basicserver/server1" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'device=server1' -d 'action=insert' ${server_url}/entity/pool/pool1 { "attrs": [], "contents": [ "/basicserver/server1" ], "driver": "pool", "name": "pool1", "parents": [], "type": "pool" } HTTP: 200 Content-type: application/json
$ ${post} -d 'device=server1' -d 'action=remove' ${server_url}/entity/pool/pool1 { "attrs": [], "contents": [], "driver": "pool", "name": "pool1", "parents": [], "type": "pool" } HTTP: 200 Content-type: application/json
Will:
- Create a pool entity called
pool1
- Create a basicserver entity called
server1
- Insert the entity
server1
into the entitypool1
- Remove the entity
server1
from the entitypool1
Examples:
$ ${post} -d 'name=pool2' ${server_url}/entity/pool [ "/pool/pool2" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'name=server2' -d 'name=server3' ${server_url}/entity/basicserver [ "/basicserver/server2", "/basicserver/server3" ] HTTP: 201 Content-type: application/json
$ ${post} -d 'device=server2' -d 'device=server3' -d 'action=insert' ${server_url}/entity/pool/pool2 { "attrs": [], "contents": [ "/basicserver/server2", "/basicserver/server3" ], "driver": "pool", "name": "pool2", "parents": [], "type": "pool" } HTTP: 200 Content-type: application/json
$ ${post} -d 'device=server2' -d 'device=server3' -d 'action=remove' ${server_url}/entity/pool/pool2 { "attrs": [], "contents": [], "driver": "pool", "name": "pool2", "parents": [], "type": "pool" } HTTP: 200 Content-type: application/json
The above will:
- Create a pool entity called
pool2
- Create two basicserver entities called
server2
andserver3
- Insert both basicserver entities into the pool entity
- Remove both basicserver entities from the pool entity
- Create a pool entity called
-
clustoapi.apps.entity.
create
(driver)[source]¶ Creates a new object of the given driver.
- Requires HTTP parameters
name
Example:
$ ${post} -d 'name=createpool1' ${server_url}/entity/pool [ "/pool/createpool1" ] HTTP: 201 Content-type: application/json
Will create a new
pool1
object with apool
driver. If thepool1
object already exists, the status code returned will be 202, and you will see whatever warnings in theWarnings
header:$ ${post_i} -d 'name=createpool1' ${server_url}/entity/pool HTTP/1.0 202 Accepted ... Warnings: Entity(s) /pool/createpool1 already exist(s)... [ "/pool/createpool1" ]
If you try to create a server of an unknown driver, you should receive a 412 status code back:
$ ${post} -d 'name=createobject' ${server_url}/entity/nondriver "Requested driver "nondriver" does not exist" HTTP: 412 Content-type: application/json
The following example:
$ ${post_i} -d 'name=createpool1' -d 'name=createpool2' ${server_url}/entity/pool HTTP/1.0 202 Accepted ... Warnings: Entity(s) /pool/createpool1 already exist(s)... [ "/pool/createpool1", "/pool/createpool2" ]
Will attempt to create new objects
createpool1
andcreatepool2
with apool
driver. As all objects are validated prior to creation, if any of them already exists the return code will be 202 (Accepted) and you will get an extra headerWarnings
with the message.- Requires HTTP parameters
-
clustoapi.apps.entity.
delete
(driver, name)[source]¶ Deletes an object if it matches the given driver
- Requires HTTP parameters
name
Examples:
$ ${post} -d 'name=servercreated' ${server_url}/entity/basicserver [ "/basicserver/servercreated" ] HTTP: 201 Content-type: application/json
$ ${delete} ${server_url}/entity/nondriver/servercreated "Requested driver "nondriver" does not exist" HTTP: 412 Content-type: application/json
$ ${delete} ${server_url}/entity/basicserver/servercreated HTTP: 204 Content-type:
$ ${delete} ${server_url}/entity/basicserver/servercreated HTTP: 404 Content-type: None
Will create a new
servercreated
object with abasicserver
driver. Then it will proceed to delete it. If the operation succeeded, it will return a 200, if the object doesn’t exist, it will return a 404.- Requires HTTP parameters
-
clustoapi.apps.entity.
list
(driver=None)[source]¶ Returns all entities, or (optionally) all entities of the given driver
Example:
$ ${get} ${server_url}/entity/ [ ... ] HTTP: 200 Content-type: application/json
Will list all entities
Example:
$ ${get} ${server_url}/entity/clustometa [ "/clustometa/clustometa" ] HTTP: 200 Content-type: application/json
Will list all entities that match the driver
clustometa
The following example should fail because there is no driver
nondriver
:$ ${get} ${server_url}/entity/nondriver "The requested driver "nondriver" does not exist" HTTP: 412 Content-type: application/json
-
clustoapi.apps.entity.
show
(driver, name)[source]¶ Returns a json representation of the given object
Example:
$ ${post} -d 'name=showpool' ${server_url}/entity/pool [ "/pool/showpool" ] HTTP: 201 Content-type: application/json
$ ${get} ${server_url}/entity/pool/showpool { "attrs": [], "contents": [], "driver": "pool", "name": "showpool", "parents": [], "type": "pool" } HTTP: 200 Content-type: application/json
Will return a JSON representation of the previously created
showpool
.$ ${get} ${server_url}/entity/basicserver/showpool "The driver for object "showpool" is not "basicserver"" HTTP: 409 Content-type: application/json
Will yield a 409 (Conflict) because the object
showpool
is not abasicserver
object.
clustoapi.apps.resourcemanager: Resource Manager Application¶
The resourcemanager
application will hold all methods related to resource
management in clusto. Pretty much allocating and deallocating resources.
-
clustoapi.apps.resourcemanager.
allocate
(driver, manager)[source]¶ This allocates a new resource to a given thing. Said thing can be either a driver (and the result will be a newly created object subclasses from this driver) or an object, and the resource manager will allocate (bind) a resource to it.
Examples:
$ ${post} -d 'name=allocator' ${server_url}/resourcemanager/simpleentitynamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simpleentitynamemanager", "name": "allocator", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json $ ${post} -d 'driver=basicserver' ${server_url}/resourcemanager/simpleentitynamemanager/allocator "/basicserver/01" HTTP: 201 Content-type: application/json
Will request a new name from the object
allocator
(which is an object ofSimpleEntityManager
that we just created with default values) and then it will create a newBasicServer
object.$ ${post} -d 'driver=basicserver' -d 'resource=99' ${server_url}/resourcemanager/simpleentitynamemanager/allocator "/basicserver/99" HTTP: 201 Content-type: application/json
Will create a new
BasicServer
object from thetestnames
resource manager with the specific name ofs99
.
-
clustoapi.apps.resourcemanager.
create
(driver)[source]¶ This differs from the standard way of creating entities is that resource managers can have a number of extra parameters added to them that not necessarily match any of the other entities. These parameters are defined by each resource manager driver and are pretty much arbitrary. Seems like a good idea to separate these crucial differences.
Examples:
$ ${post} -d 'name=nameman1' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman1", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json
Will create a
SimpleNameManager
resource manager namednamemgr1
with all default values set.$ ${post} -d 'name=ipman1' -d 'gateway=192.168.1.1' -d 'netmask=255.255.255.0' -d 'baseip=192.168.1.10' ${server_url}/resourcemanager/ipmanager { "attrs": [ { "datatype": "string", "key": "baseip", "number": null, "subkey": "property", "value": "192.168.1.10" }, { "datatype": "string", "key": "gateway", "number": null, "subkey": "property", "value": "192.168.1.1" }, { "datatype": "string", "key": "netmask", "number": null, "subkey": "property", "value": "255.255.255.0" } ], "contents": [], "count": 0, "driver": "ipmanager", "name": "ipman1", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json
Will create a
IPManager
resource manager namedipman1
with some additional arguments such asnetmask
,gateway
andbaseip
-
clustoapi.apps.resourcemanager.
deallocate
(driver, manager)[source]¶ Resource managers should allow you to deallocate things just the same as allocating things.
Examples:
$ ${post} -d 'name=ipman2' -d 'gateway=192.168.1.1' -d 'netmask=255.255.255.0' -d 'baseip=192.168.1.10' ${server_url}/resourcemanager/ipmanager { "attrs": [ { "datatype": "string", "key": "baseip", "number": null, "subkey": "property", "value": "192.168.1.10" }, { "datatype": "string", "key": "gateway", "number": null, "subkey": "property", "value": "192.168.1.1" }, { "datatype": "string", "key": "netmask", "number": null, "subkey": "property", "value": "255.255.255.0" } ], "contents": [], "count": 0, "driver": "ipmanager", "name": "ipman2", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json $ ${post} -d 'name=names2' -d 'basename=a' ${server_url}/resourcemanager/simpleentitynamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simpleentitynamemanager", "name": "names2", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json $ ${post} -d 'driver=basicserver' ${server_url}/resourcemanager/simpleentitynamemanager/names2 "/basicserver/a01" HTTP: 201 Content-type: application/json $ ${post} -d 'object=a01' ${server_url}/resourcemanager/ipmanager/ipman2 { "datatype": "int", "key": "ip", "number": 0, "subkey": null, "value": 1084752130 } HTTP: 201 Content-type: application/json $ ${delete} -d 'object=a01' ${server_url}/resourcemanager/ipmanager/ipman2 HTTP: 204 Content-type:
-
clustoapi.apps.resourcemanager.
list
(driver=None)[source]¶ Lists all resource managers found in the clusto database. Optionally you can list all resource managers that match the given
driver
Examples:
$ ${post} -d 'name=zmanager' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "zmanager", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json
The above will create a simple name manager called “zmanager”
$ ${get} ${server_url}/resourcemanager/ [ "/simpleentitynamemanager/testnames", "/simplenamemanager/zmanager" ] HTTP: 200 Content-type: application/json
The above will list all resource managers in clusto, which should have “zmanager”
$ ${get} ${server_url}/resourcemanager/simpleentitynamemanager [ "/simpleentitynamemanager/testnames" ] HTTP: 200 Content-type: application/json
Will list all resource managers of driver
SimpleNameManager
$ ${get} ${server_url}/resourcemanager/notadriver "Not a valid driver "notadriver" (driver name notadriver doesn't exist.)" HTTP: 404 Content-type: application/json
Will return a
404
error because that resource manager driver doesn’t exist
-
clustoapi.apps.resourcemanager.
show
(driver, manager)[source]¶ Shows the details of the given resource manager, if it is a resource manager
Examples:
$ ${post} -d 'name=nameman2' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman2", "parents": [], "type": "resourcemanager" } HTTP: 201 Content-type: application/json $ ${get} ${server_url}/resourcemanager/simplenamemanager/nameman1 { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman1", "parents": [], "type": "resourcemanager" } HTTP: 200 Content-type: application/json
Will create the
nameman2
resource manager, then show its details. In this case both operations yield the same data.$ ${get} ${server_url}/resourcemanager/simpleentitynamemanager/nonames "Object "nonames" not found (nonames does not exist.)" HTTP: 404 Content-type: application/json
Will return a
404
error since the resource manager wasn’t found$ ${get} ${server_url}/resourcemanager/nomanager/testnames "The driver "nomanager" is not a valid driver" HTTP: 412 Content-type: application/json
Will return a
412
because the drivernomanager
doesn’t exist$ ${get} ${server_url}/resourcemanager/basicserver/testserver1 "The object "testserver1" is not a resource manager" HTTP: 409 Content-type: application/json
Will return a
412
instead because even though the driverbasicserver
exists, it is not a resource manager driver
Developer documentation¶
Releasing a new version¶
Once you are comfortable with the changes to be released, there is a few things you have to do to consider this software “released”.
It is worth mentioning that I will very probably script all (or almost all) of this, once I have the time / decide how to do it.
Pre-requisites¶
- You need a github account with permissions for pushing new tags/releases
- You need to be added to the PyPi project so you can submit new releases
- Your
~/.pypirc
is configured with your PyPi username and password - You need your GPG key signed by one of the other maintainers
Cutting the release¶
Usually involves a few steps (for simplicity, using version 0.0.1 to illustrate the examples)
- Updating the changelog: You create a new page in
docs/changelog
nameddocs/changelog/v0.0.1.rst
- Re-linking current changelog to new changelog: You re-point the symlink
from
docs/changelog/current.rst
todocs/changelog/v0.0.1.rst
- Commit your changelog change
- Update the version: Need to bump the version in
clustoapi/__init__.py
to reflect the new 0.0.1 version - Commit your version bump change
- Tag the release: You need to
git tag -s v0.0.1
at this point. Pay attention to the-s
switch, this implies the tag will be signed with your GPG key (optionally, use-u
to specify which key to use if you don’t want to use the default)
Publishing the release¶
This entails publishing the release on github. Once you publish the release on github, a tweet will be posted shortly (< 1 hour after) to the @clustodotorg account.
- Push your changes up to the repo: You need to both
git push
andgit push --tags
for this operation, your changes should be visible now, and the new tag should appear in https://github.com/clusto/clusto-apiserver/tags - Edit the tag so it turns into a release in github: Draft a new release
https://github.com/clusto/clusto-apiserver/releases/new and pick the tag
you recently created. The title of the release should be “Version 0.0.1”
(to continue with the sample version) and the body should be the contents
of the
docs/changelog/v0.0.1.rst
file. Keep in mind that the format is Markdown while the documentation is ReStructured Text, so adjust as necessary (mainly titles change i.e. replace^
for-
)
At this point the release should appear in the github releases page https://github.com/clusto/clusto-apiserver/releases/
Uploading to PyPi¶
Finally, publishing the module / source code to PyPi should be a single step:
Sign and upload the new version tarball: Basically run (from the project’s root dir):
python setup.py sdist upload --sign [--identity <GPG identity>]
What this does is:
- Creates a tarball under the
dist/
directory (thesdist
command) - Using the
--sign
flag signs the tarball using your GPG key (depending on your setup, you may get prompted for your GPG password). Optionally, you can use the--identity
argument if you aren’t using your default identity to sign - Uploads the recently created and signed tarball to PyPi (the
upload
command)
- Creates a tarball under the
At this point, the new version should be visible in https://pypi.python.org/pypi/clusto-apiserver
Full Changelog History¶
Version 0.2.0 (2014-09-25)¶
First “real” release, including additional tests and finally some PR contributions :) (Thanks James!)
Features¶
- Split install requirements into 3 files, which can be read either by
pip
or by thesetup.py
script.requirements.txt
is the usual file you install fromtest-requirements.txt
is a file intended for use when you are testing this repo.dev-requirements.txt
is the file you should install from if you are hacking in this repo.
- Added a custom
Clusto-Mode
header to change how the JSON data is presented back:compact
mode: is the default for API calls that return a list of items back to the requestor.expanded
mode: is the default for API calls that return a single item back to the requestor
- Added a
by-names
endpoint, it receives a list of parameters and will return back a list of items found
Version 0.2.1 (2014-09-26)¶
Small bugfix release
Bugfixes¶
- Adding
requirements.txt
,dev-requirements.txt
andtest-requirements.txt
to the distribution tarball for source downloads
Version 0.3.0 (2015-02-13)¶
Feature release
Features¶
- The
/by-attr
endpoint, which translates to the main clusto query methodget_by_attr()
- Pagination implementation for results. By default it is turned off, basically only useful for those cases where your data is still too much. One important caveat here is that this only affects the presentation of the data back to the client, not the querying of the data from the database. Pagination is not native to clusto, yet
- Minify implementation for results. All results by default are pretty printed but if you pass a request header they will be printed as a single string
Version 0.3.1 (2015-02-13)¶
New release only to deal with dumb pypi upload error hurr.
Version 0.4.0 (2015-05-27)¶
New feature release with some API changes (read at the bottom) which will hopefully stop being a thing once we hit 1.0
Features¶
- Adding a main /driverlist endpoint to match clusto.driverlist
- Added support for extra server adapter options in config
- Better test cases for resource manager core application
- Support for pool removal with caveats, read below for changes
API Changes¶
- Pool insertion changed from a PUT to a generic POST action, briefly: PUT
for
.insert()
didn’t make much sense without support for.remove()
but.remove()
isn’t properly a DELETE action. Ergo we changed it to be a generic POST to the endpoint with anaction=[insert,remove]
field
Version 0.4.1 (2015-06-04)¶
New feature release
Features¶
- Add get_ips list to the show method (James Cunningham)
- Add ability to post json bodies in attribute app (James Cunningham)
- Added expansion and paging to the entity app (James Cunningham)
Version 0.4.2 (2015-06-18)¶
New bugfix release
Bug fixes¶
- lists and dicts attribute values were returned as a unicode string representation which was unparseable as a regular json object.
Version 0.5.0 (2015-07-24)¶
New feature release
Features¶
- add and set attr marriage, bring a present (James Cunningham)
- add typecast util (James Cunningham)
- add ability to set global response headers in clusto.conf (James Cunningham)