For some reason, the idea of direct interaction with the various OpenStack components seemed like a good idea. The aim was to create an instance, set sane security rules, and add a public key all through the API.

I struggled with the documentation. It was a little hard to find, and what I did find seemed a little thin. However with the –debug option on the cli clients, which prints out all of the calls made to the various API endpoints as it goes along, it was game on.

First things first, we need an OpenStack environment to play with. RedHat with their newly released RDO comes to the rescue here. Out of all the one click OpenStack tools I’ve tried, RDO has been the simplest by far. Just three simple steps. Well, if you don’t include step 0 of course: http://openstack.redhat.com/Quickstart . We also installed a machine image through the dashboard to make life a little easier.

OpenStack LogoMigrate to the cloud! Talk to use about OpenStack powered, private cloud deployments. Find out more

With a successful deployment we end up with the following credentials for our testbed (yes I know creating instances as admin is bad, and if we were using quantum we would all be going to hell, but bare with me):

user: admin
tenant name: admin
password: 0002472e1ab140f2

Second thing second, piping the output from curl through the python JSON module made life a lot easier, no longer starting cross eyed at the screen trying to follow the curly brace nesting.

Where to start? OpenStack uses a component called Keystone for all authentication, this is documented well and in numerous places, so for this I’ll simply state that before we can do anything we need to obtain an authentication token for the user & project that the instance will belong to. This simply requires a POST to the Keystone server with the following JSON structure:

{
    "auth": {
        "tenantName": "admin", 
        "passwordCredentials": {
            "username": "admin", 
            "password": "0002472e1ab140f2" 
        }
    }
}

Which will result in the following command (the keystone endpoint in my case is at http://10.199.0.250:35357/v2.0 and we are after a token):

curl http://10.199.0.250:35357/v2.0/tokens \
-X POST -H "Content-Type: application/json" \
-d '{"auth": {"tenantName": "admin", "passwordCredentials": {"username": "admin", "password": "0002472e1ab140f2"}}}'

Which will return the following mess:

{"access": {"token": {"issued_at": "2013-04-18T14:40:23.299903", "expires": "2013-04-19T14:40:23Z", "id": "4c5ef01f52c7404fb5324c520d25d1fe", "tenant": {"description": "admin tenant", "enabled": true, "id": "51ad87714b86442d9a74537d6f890060", "name": "admin"}}, "serviceCatalog": [{"endpoints": [{"adminURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060", "region": "RegionOne", "internalURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060", "id": "9869f55f0de2490685676b6ec27f6097", "publicURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060"}], "endpoints_links": [], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://10.199.0.250:8080", "region": "RegionOne", "internalURL": "http://10.199.0.250:8080", "id": "321601d827ba4bbbb6de1df69fd43a1c", "publicURL": "http://10.199.0.250:8080"}], "endpoints_links": [], "type": "s3", "name": "swift_s3"}, {"endpoints": [{"adminURL": "http://10.199.0.250:9292", "region": "RegionOne", "internalURL": "http://10.199.0.250:9292", "id": "cca7d7a24dbe45b6ae08da2c023b0d82", "publicURL": "http://10.199.0.250:9292"}], "endpoints_links": [], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060", "region": "RegionOne", "internalURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060", "id": "14773153229d4e7f80e47cf7b1dd2d15", "publicURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060"}], "endpoints_links": [], "type": "volume", "name": "cinder"}, {"endpoints": [{"adminURL": "http://10.199.0.250:8773/services/Admin", "region": "RegionOne", "internalURL": "http://10.199.0.250:8773/services/Cloud", "id": "064df72a67f54dffa68c07b8fc400bdb", "publicURL": "http://10.199.0.250:8773/services/Cloud"}], "endpoints_links": [], "type": "ec2", "name": "nova_ec2"}, {"endpoints": [{"adminURL": "http://10.199.0.250:8080/", "region": "RegionOne", "internalURL": "http://10.199.0.250:8080/v1/AUTH_51ad87714b86442d9a74537d6f890060", "id": "194df182a8c043e48175a40fb615064e", "publicURL": "http://10.199.0.250:8080/v1/AUTH_51ad87714b86442d9a74537d6f890060"}], "endpoints_links": [], "type": "object-store", "name": "swift"}, {"endpoints": [{"adminURL": "http://10.199.0.250:35357/v2.0", "region": "RegionOne", "internalURL": "http://10.199.0.250:5000/v2.0", "id": "34db74b5f32f4121932725b1146a1701", "publicURL": "http://10.199.0.250:5000/v2.0"}], "endpoints_links": [], "type": "identity", "name": "keystone"}], "user": {"username": "admin", "roles_links": [], "id": "b5902682120742baa150945d8a37ff47", "roles": [{"name": "admin"}], "name": "admin"}, "metadata": {"is_admin": 0, "roles": ["9aa2eb385f4e4a8e80ad5002c212e76b"]}}}

but with a little help from Python, by piping it through the json module, it can be turned into:

{
    "access": {
        "metadata": {
            "is_admin": 0, 
            "roles": [
                "9aa2eb385f4e4a8e80ad5002c212e76b"
            ]
        }, 
        "serviceCatalog": [
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060", 
                        "id": "9869f55f0de2490685676b6ec27f6097", 
                        "internalURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060", 
                        "publicURL": "http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "nova", 
                "type": "compute"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:8080", 
                        "id": "321601d827ba4bbbb6de1df69fd43a1c", 
                        "internalURL": "http://10.199.0.250:8080", 
                        "publicURL": "http://10.199.0.250:8080", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "swift_s3", 
                "type": "s3"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:9292", 
                        "id": "cca7d7a24dbe45b6ae08da2c023b0d82", 
                        "internalURL": "http://10.199.0.250:9292", 
                        "publicURL": "http://10.199.0.250:9292", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "glance", 
                "type": "image"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060", 
                        "id": "14773153229d4e7f80e47cf7b1dd2d15", 
                        "internalURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060", 
                        "publicURL": "http://10.199.0.250:8776/v1/51ad87714b86442d9a74537d6f890060", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "cinder", 
                "type": "volume"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:8773/services/Admin", 
                        "id": "064df72a67f54dffa68c07b8fc400bdb", 
                        "internalURL": "http://10.199.0.250:8773/services/Cloud", 
                        "publicURL": "http://10.199.0.250:8773/services/Cloud", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "nova_ec2", 
                "type": "ec2"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:8080/", 
                        "id": "194df182a8c043e48175a40fb615064e", 
                        "internalURL": "http://10.199.0.250:8080/v1/AUTH_51ad87714b86442d9a74537d6f890060", 
                        "publicURL": "http://10.199.0.250:8080/v1/AUTH_51ad87714b86442d9a74537d6f890060", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "swift", 
                "type": "object-store"
            }, 
            {
                "endpoints": [
                    {
                        "adminURL": "http://10.199.0.250:35357/v2.0", 
                        "id": "34db74b5f32f4121932725b1146a1701", 
                        "internalURL": "http://10.199.0.250:5000/v2.0", 
                        "publicURL": "http://10.199.0.250:5000/v2.0", 
                        "region": "RegionOne"
                    }
                ], 
                "endpoints_links": [], 
                "name": "keystone", 
                "type": "identity"
            }
        ], 
        "token": {
            "expires": "2013-04-19T14:43:25Z", 
            "id": "94ad2a44098444baa0550528f2662c57", 
            "issued_at": "2013-04-18T14:43:25.396925", 
            "tenant": {
                "description": "admin tenant", 
                "enabled": true, 
                "id": "51ad87714b86442d9a74537d6f890060", 
                "name": "admin"
            }
        }, 
        "user": {
            "id": "b5902682120742baa150945d8a37ff47", 
            "name": "admin", 
            "roles": [
                {
                    "name": "admin"
                }
            ], 
            "roles_links": [], 
            "username": "admin"
        }
    }
}

We’re only really interested in the unique identifier for the authentication token:

token.id: "94ad2a44098444baa0550528f2662c57"

and the tenant id:

token.id.tenant.id: "51ad87714b86442d9a74537d6f890060"

With these two ids we’re now pretty much set.

Before I forget, lets open up the firewall to allow SSH access. You can get the existing security group rules with the following, note the project id in the url, as well as the two headers including the tenant id and authentication token:

curl -s http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/os-security-groups \
-X GET \
-H "X-Auth-Project-Id: admin" \
-H "Accept: application/json" \
-H "X-Auth-Token: 9d88f6c9f40a4890aa8612fda095bccb" | python -mjson.tool

To add port 22 to the list of allowed, we need to provide the following data structure:

 "security_group_rule": {
    "from_port": "22", 
    "ip_protocol": "tcp", 
    "to_port": "22", 
    "parent_group_id": 1, 
    "cidr": "0.0.0.0/0", 
    "group_id": null
}

so the curl command would look like:

curl -s http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/os-security-group-rules \
-X POST -H "X-Auth-Project-Id: admin" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Auth-Token: 9d88f6c9f40a4890aa8612fda095bccb" \
-d '{"security_group_rule": {"from_port": "22", "ip_protocol": "tcp", "to_port": "22", "parent_group_id": 1, "cidr": "0.0.0.0/0", "group_id": null}}' | python -mjson.tool

Next is to import an SSH key so that we can log in:

curl -i http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/os-keypairs \
-X POST -H "X-Auth-Project-Id: admin" \
-H "Content-Type: application/json" \
-H "Accept: application/json" -H "X-Auth-Token: 4521d6dd1ece458f89f0c056442765b0" \
-d '{"keypair": {"public_key": "YOUR SSH PUBLIC KEY HERE", "name": "test-key"}}'

Woohoo, nearly there. So, with that all set up, we can now launch an instance by providing the following object:

"server": {
    "name": "instance1", 
    "imageRef": "992f5732-af50-40fc-987f-25951cbce943", 
    "key_name": "test-key", 
    "flavorRef": "3", 
    "max_count": 1, 
    "min_count": 1
}

The two magic numbers in there are the imageRef and the flavorRef which relate to the unique id for the machine image we created earlier and the number of the instance size we wich to use. These can be obtained via the API with the following two calls:

CatN EnterpriseFind out how cloud innovation can drive efficiencies in your business.Find out more

Available images:

curl -s http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/images \
-X GET \
-H "X-Auth-Project-Id: admin" \
-H "Accept: application/json" \
-H "X-Auth-Token: 6083193874684f38865b086a8a5f4b7b" | python -mjson.tool

Available flavors:

curl -s http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/flavors \
-X GET \
-H "X-Auth-Project-Id: admin" \
-H "Accept: application/json" \
-H "X-Auth-Token: 6083193874684f38865b086a8a5f4b7b" | python -mjson.tool

Which result in the following call to actually create the instance:

curl -i http://10.199.0.250:8774/v2/51ad87714b86442d9a74537d6f890060/servers \
-X POST -H "X-Auth-Project-Id: admin" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-Auth-Token: 6083193874684f38865b086a8a5f4b7b" \
-d '{"server": {"name": "instance1", "imageRef": "992f5732-af50-40fc-987f-25951cbce943", "key_name": "damion-flybook", "flavorRef": "3", "max_count": 1, "min_count": 1}}'

Job done.

Damion Parry Systems Administrator

Damion works in DevOps on emerging cloud technologies and how they can be integrated with the existing CatN platform. You can find him on Google+.