Source code for clustoapi.apps.resourcemanager

#!/usr/bin/env python
#
# -*- mode:python; sh-basic-offset:4; indent-tabs-mode:nil; coding:utf-8 -*-
# vim:set tabstop=4 softtabstop=4 expandtab shiftwidth=4 fileencoding=utf-8:
#
# Copyright 2013, Jorge Gallegos <kad@blegh.net>

"""
The ``resourcemanager`` application will hold all methods related to resource
management in clusto. Pretty much allocating and deallocating resources.
"""

import bottle
from bottle import request
import clusto
from clusto import drivers
from clustoapi import util


app = bottle.Bottle(autojson=False)
app.config['source_module'] = __name__


def _get_resource_manager(manager, driver):
    "A wrapper because an extra check has to be made :("

    obj, status, msg = util.get(manager, driver)
    if obj:
        if not issubclass(obj.__class__, drivers.resourcemanagers.ResourceManager):
            msg = 'The object "%s" is not a resource manager' % (manager,)
            status = 409
            obj = None
        else:
            pass
    else:
        pass

    return obj, status, msg


@app.get('/')
@app.get('/<driver>')
@app.get('/<driver>/')
[docs]def list(driver=None): """ Lists all resource managers found in the clusto database. Optionally you can list all resource managers that match the given ``driver`` Examples: .. code:: bash $ ${post} -d 'name=zmanager' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "zmanager", "parents": [] } HTTP: 201 Content-type: application/json The above will create a simple name manager called "zmanager" .. code:: bash $ ${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" .. code:: bash $ ${get} ${server_url}/resourcemanager/simpleentitynamemanager [ "/simpleentitynamemanager/testnames" ] HTTP: 200 Content-type: application/json Will list all resource managers of driver ``SimpleNameManager`` .. code:: bash $ ${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 """ result = [] if driver: try: ents = clusto.get_entities(clusto_drivers=[driver]) except NameError as ne: return util.dumps( 'Not a valid driver "%s" (%s)' % (driver, ne,), 404 ) else: # Until we fix the ipmanager snafu, gotta check for both types ents = clusto.get_entities(clusto_types=['resourcemanager']) for ent in ents: # Kind of shitty way, but have to make sure these are all resource managers if issubclass(ent.__class__, drivers.resourcemanagers.ResourceManager): result.append(util.unclusto(ent)) return util.dumps(result)
@app.post('/<driver>')
[docs]def create(driver): """ 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: .. code:: bash $ ${post} -d 'name=nameman1' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman1", "parents": [] } HTTP: 201 Content-type: application/json Will create a ``SimpleNameManager`` resource manager named ``namemgr1`` with all default values set. .. code:: bash $ ${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": [] } HTTP: 201 Content-type: application/json Will create a ``IPManager`` resource manager named ``ipman1`` with some additional arguments such as ``netmask``, ``gateway`` and ``baseip`` """ if driver not in clusto.driverlist: return util.dumps('Requested driver "%s" does not exist' % (driver,), 412) cls = clusto.driverlist[driver] name = request.params.get('name') request.params.pop('name') # Pass any additional parameters as is to the constructor kwargs = {} for param, value in request.params.items(): kwargs[param] = value found = None try: found = util.unclusto(clusto.get_by_name(name)) except LookupError: pass obj = clusto.get_or_create(name, cls, **kwargs) headers = {} if found: headers['Warnings'] = 'Resource manager "%s" already exists' % (found,) code = 201 if found: code = 202 return util.dumps(util.show(obj), code, headers=headers)
@app.get('/<driver>/<manager>') @app.get('/<driver>/<manager>/')
[docs]def show(driver, manager): """ Shows the details of the given resource manager, if it is a resource manager Examples: .. code:: bash $ ${post} -d 'name=nameman2' ${server_url}/resourcemanager/simplenamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman2", "parents": [] } HTTP: 201 Content-type: application/json $ ${get} ${server_url}/resourcemanager/simplenamemanager/nameman1 { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simplenamemanager", "name": "nameman1", "parents": [] } 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. .. code:: bash $ ${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 .. code:: bash $ ${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 driver ``nomanager`` doesn't exist .. code:: bash $ ${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 driver ``basicserver`` exists, it is not a resource manager driver """ obj, status, msg = _get_resource_manager(manager, driver) if not obj: return util.dumps(msg, status) else: return util.dumps(util.show(obj))
@app.post('/<driver>/<manager>')
[docs]def allocate(driver, manager): """ 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: .. code:: bash $ ${post} -d 'name=allocator' ${server_url}/resourcemanager/simpleentitynamemanager { "attrs": [ ... ], "contents": [], "count": 0, "driver": "simpleentitynamemanager", "name": "allocator", "parents": [] } 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 of ``SimpleEntityManager`` that we just created with default values) and then it will create a new ``BasicServer`` object. .. code:: bash $ ${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 the ``testnames`` resource manager with the specific name of ``s99``. """ obj, status, msg = _get_resource_manager(manager, driver) if not obj: return util.dumps(msg, status) else: d = request.params.get('driver') o = request.params.get('object') thing = d or o if not thing: return util.dumps( 'Cannot allocate an empty thing, send one of ' '"driver", "object"', 404 ) if d: thing = clusto.driverlist.get(thing) else: thing = clusto.get_by_name(thing) if not thing: return util.dumps('Thing was "%s" not found' % (d or o,), 404) resource = request.params.get('resource', default=()) r = obj.allocate(thing, resource) # The returned value can be anything such a string, number, or attribute return util.dumps(util.unclusto(r), 201)
@app.delete('/<driver>/<manager>')
[docs]def deallocate(driver, manager): """ Resource managers should allow you to deallocate *things* just the same as allocating *things*. Examples: .. code:: bash $ ${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": [] } 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": [] } 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: """ resman, status, msg = _get_resource_manager(manager, driver) if not resman: return util.dumps(msg, status) else: obj = request.params.get('object') if not obj: return util.dumps( 'Cannot deallocate empty, send an object to deallocate', 404 ) obj, status, msg = util.get(obj) resource = request.params.get('resource', ()) # Attempt to deallocate resman.deallocate(obj, resource=resource) return util.dumps(util.unclusto(resman), 204)