Quantcast
Channel: storage – RDO Community Blogs
Viewing all articles
Browse latest Browse all 11

Swift discoverable capabilities

$
0
0

During our investigation for the next update of the CloudWatt platform, we identified a recent patch that has been merged in Swift that introduces discoverable capabilities, allowing clients to retrieve configuration info programmatically. The info published is akin to which middlewares are installed or what version of Swift is running. In this article I will describe how it works, how to admin-protect some parts of the configuration options and how to add custom entries to the published info.

A new endpoint: /info

The patch introduces a new endpoint: ‘/info’. This endpoint listens for unauthenticated GET requests and returns a list of key/values representing the info of the cluster and formatted as JSON. Here’s the simplest example, on a recent devstack:

$ curl -X GET http://192.168.33.10:8080/info
{"formpost": {}, "bulk_delete": {"max_failed_deletes": 1000, "max_deletes_per_request": 10000}, "container_quotas": {}, "swift": {"version": "1.10.0.196.g55dafa2"}, "tempurl": {}, "bulk_upload": {"max_failed_extractions": 1000, "max_containers_per_extraction": 10000}, "ratelimit": {}, "slo": {}, "account_quotas": {}, "staticweb": {}, "keystoneauth": {}, "tempauth": {}}%

or, using a nice formatting:

$ curl -X GET http://192.168.33.10:8080/info | python -m json.tool
{
    "account_quotas": {},
    "bulk_delete": {
        "max_deletes_per_request": 10000,
        "max_failed_deletes": 1000
    },
    "bulk_upload": {
        "max_containers_per_extraction": 10000,
        "max_failed_extractions": 1000
    },
    "container_quotas": {},
    "formpost": {},
    "keystoneauth": {},
    "ratelimit": {},
    "slo": {},
    "staticweb": {},
    "swift": {
        "version": "1.10.0.196.g55dafa2"
    },
    "tempauth": {},
    "tempurl": {}
}

In these results, we can see that all the activated middleware are represented as records in the key/value map. Notice that the bulk middleware has opted for publishing two separate entries each of which publishes 2 values.

Note that it’s also possible to retrieve the headers only with a HEAD request, or the list of the allowed methods with OPTIONS, like this:

$ curl -I -X HEAD http://192.168.33.10:8080/info
HTTP/1.1 200 OK
Content-Length: 371
Content-Type: application/json; charset=UTF-8
X-Trans-Id: txe2d18226322f4bd099044-0052a63d19
Date: Mon, 09 Dec 2013 21:58:49 GMT

$ curl -I -X OPTIONS http://192.168.33.10:8080/info
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Allow: HEAD, GET, OPTIONS
Content-Length: 0
X-Trans-Id: txffec90b0a3cd461f93e24-0052a63d0d
Date: Mon, 09 Dec 2013 21:58:37 GMT

Publish info from your middleware

The patch also introduced a function in swift.common.utils called swift_register_info that allows adding entries to the info JSON. The first argument of the command specifies an entry in the JSON response. If the key doesn’t exist, it’s created.

Let’s look at an example of how to use it, by writing an empty middleware and registering an entry in the info:

from swift.common.utils import register_swift_info


class Middleware(object):
    def __init__(self, app, conf, *args, **kwargs):
        self.app = app
        self.conf = conf

    def __call__(self, env, start_response):
        return self.app(engv, start_response)

def filter_factory(global_conf, **local_conf):
    conf = global_conf.copy()
    conf.update(local_conf)

    register_swift_info("my_new_middleware")
    return lambda app: Middleware(app, conf)

As you can see, the filter function calls register_swift_info to declare an entry to add to info. That’s all there is to it. And we can find the info in the JSON response:

$ curl -X GET http://192.168.33.10:8080/info | python -m json.tool
{
    "account_quotas": {},
    "bulk_delete": {
        "max_deletes_per_request": 10000,
        "max_failed_deletes": 1000
    },
    "bulk_upload": {
        "max_containers_per_extraction": 10000,
        "max_failed_extractions": 1000
    },
    "container_quotas": {},
    "formpost": {},
    "keystoneauth": {},
    "my_new_middleware": {},
    "ratelimit": {},
    "slo": {},
    "staticweb": {},
    "swift": {
        "version": "1.10.0.196.g55dafa2"
    },
    "tempauth": {},
    "tempurl": {}
}

register_swift_info takes other arguments, allowing the publication of more complex info. We’ve already seen the result of the bulk middleware publishing data about its own configuration. Here’s how it is used in the code:

register_swift_info(
    'bulk_upload',
    max_containers_per_extraction=max_containers_per_extraction,
    max_failed_extractions=max_failed_extractions)
register_swift_info(
    'bulk_delete',
    max_deletes_per_request=max_deletes_per_request,
    max_failed_deletes=max_failed_deletes)

Link to the source

Admin-protected information

There’s one more argument to the register_swift_info function that deserves mentioning. It’s a named argument called admin that expects a boolean and is False by default. An info registered with admin set to True will not be included in the standard GET /info response, unless it is accompanied by a HMAC signature. If you’re familiar with the Swift middlewares TempURL or Formpost, it’s a similar process to sign these requests.

Let’s rewrite our middleware so that it registers an admin protected entry:

from swift.common.utils import register_swift_info


class Middleware(object):
    def __init__(self, app, conf, *args, **kwargs):
        self.app = app
        self.conf = conf

    def __call__(self, env, start_response):
        return self.app(env, start_response)

def filter_factory(global_conf, **local_conf):
    conf = global_conf.copy()
    conf.update(local_conf)

    register_swift_info("my_new_middleware", admin=True)
    return lambda app: Middleware(app, conf)

The entry has now disappeared from the response to unsigned GET requests:

$ curl http://192.168.33.10:8080/info | python -m json.tool
{
    "account_quotas": {},
    "bulk_delete": {
        "max_deletes_per_request": 10000,
        "max_failed_deletes": 1000
    },
    "bulk_upload": {
        "max_containers_per_extraction": 10000,
        "max_failed_extractions": 1000
    },
    "container_quotas": {},
    "formpost": {},
    "keystoneauth": {},
    "ratelimit": {},
    "slo": {},
    "staticweb": {},
    "swift": {
        "version": "1.10.0.196.g55dafa2"
    },
    "tempauth": {},
    "tempurl": {}
}

We’ll need to generate a valid signature to retrieve all the info. Here’s a small snippet of Python code that can do it:

import hmac
from hashlib import sha1
import time


def get_hmac(method, path, expires, key):
    return hmac.new(
        key, '%s\n%s\n%s' % (method, expires, path), sha1).hexdigest()

Note that this function is available in swift.common.utils. A quick overview of the arguments:

  • method: The HTTP method/verb we’re using
  • path: the endpoint. In our case that’s ‘/info’
  • expires: a Unix timestamp that is the limit of validity
  • key: a shared secret key that can be found in the proxy-server conf file (usually in /etc/swift/proxy-server.conf)

Note if the secret key isn’t present in the conf file, admin calls to /info are disabled.

Here’s how we can retrieve a signature valid for 10 minutes:

method = "GET"
path = "/info"
expires = int(time.time() + 600) # valid for 600 seconds
key = "secret_admin_key"

print(get_hmac(method, path, expires, key))
print(expires)

Result:

62587fca8ec27a0acdf60e7fea03fd0d06999274
1386669556

Now using these in a request header I can retrieve the admin info:

$ curl http://192.168.33.10:8080/info -H 'swiftinfo_sig:62587fca8ec27a0acdf60e7fea03fd0d06999274' -H 'swiftinfo_expires:1386669556' | python -m json.tool
{
    "account_quotas": {},
    "bulk_delete": {
        "max_deletes_per_request": 10000,
        "max_failed_deletes": 1000
    },
    "bulk_upload": {
        "max_containers_per_extraction": 10000,
        "max_failed_extractions": 1000
    },
    "container_quotas": {},
    "formpost": {},
    "keystoneauth": {},
    "my_new_middleware": {},
    "ratelimit": {},
    "slo": {},
    "staticweb": {},
    "swift": {
        "version": "1.10.0.196.g55dafa2"
    },
    "tempauth": {},
    "tempurl": {}
}

In Conclusion

This feature is one of the new features presented in the latest version of Swift, the recently published 1.11.0. If you want to know more, you can read what the programmers working on it had to say about it:


Viewing all articles
Browse latest Browse all 11

Latest Images

Trending Articles





Latest Images