1. Introduction
  2. Get OpenStack
  3. Get jclouds
  4. Terminology
  5. Keystone v2-v3 authentication
  6. Nova: List Servers
  7. Swift: Use Containers
  8. Next Steps
  9. OpenStack Dependencies

Introduction

OpenStack is a global collaboration of developers and cloud computing technologists producing the ubiquitous open source cloud computing platform for public and private clouds. The project aims to deliver solutions for all types of clouds by being simple to implement, massively scalable, and feature rich. The technology consists of a series of interrelated projects delivering various components for a cloud infrastructure solution.

Get OpenStack

You can either install a private OpenStack cloud for yourself or use an existing OpenStack public cloud.

Private Clouds

If you don't have a private OpenStack cloud but still want to try it out, you can use DevStack to create your own mini-OpenStack cloud.

Public Clouds

Because the OpenStack API is also open, the jclouds APIs that talk to private OpenStack clouds work just as well with public OpenStack clouds. OpenStack is used by several large public clouds, both the HP Cloud (HP Cloud Getting Started Guide) and Rackspace Cloud (Rackspace Getting Started Guide) are based on it. If you don't want to sign up for a paid public cloud, you can use TryStack.

Get jclouds

  1. Ensure you are using the Java Development Kit (JDK) version 6 or later.
    • javac -version
  2. Ensure you are using Maven version 3 or later.
    • mvn -version
  3. Create a directory to try out jclouds.
    • mkdir jclouds
    • cd jclouds
  4. Make a local copy of the pom.xml file below in the jclouds directory.
    • mvn dependency:copy-dependencies "-DoutputDirectory=./lib"
  5. You should now have a directory with the following structure:
    • jclouds/
      • pom.xml
      • lib/
        • *.jar

Terminology

There are some differences in terminology between jclouds and OpenStack that should be made clear.

jclouds OpenStack
Compute Nova
Node Server
Location/Region Region
Hardware Flavor
NodeMetadata Server details
UserMetadata Metadata
BlobStore Swift
Blob File

Keystone v2-v3 authentication

OpenStack Keystone (aka: OpenStack Identity Service) has major changes between v2 and v3 (detail. Identity API v2.0 and v3 History).

To login, provide:

jclouds provides backward compatibility between Keystone v2 and v3, but you should keep the following in mind to fully understand authentication against your OpenStack platform (See also the recent OpenStack Keystone v3 Support blog post).

v2

This snippet:

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "2");

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v2.0")
   .credentials("myTenant:foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

or

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "2");
overrides.put(KeystoneProperties.TENANT_NAME, "myTenant");

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v2.0")
   .credentials("foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

Will produce when authentication needed:

POST https://host:5000/v2.0/tokens HTTP/1.1
{
    "auth": {
        "passwordCredentials": {
            "username": "foo",
            "password": "bar"
        },
        "tenantName": "myTenant"
    }
}    

v3: Default (unscoped)

Keystone v3 requires at min a user authentication domain (generally the one you are using to login through UI console), so this snippet:

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "3");

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v3")
   .credentials("ldap:foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

Will produce when authentication needed:

POST https://host:5000/v3/auth/tokens HTTP/1.1
{
    "auth": {
        "identity": {
            "methods": ["password"],
            "password": {
                "user": {
                    "name": "foo",
                    "domain": {
                        "name": "ldap"
                    },
                    "password": "bar"
                }
            }
        },
        "scope": "unscoped"
    }
}

In this case, no project (previously tenant in OpenStack keystone v2) is provided.

v3: Project-scoped

A common usage of OpenStack keystone v3 is to provide the project scope, this snippet:

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "3");
overrides.put(KeystoneProperties.SCOPE, "project:myTenant");

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v3")
   .credentials("ldap:foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

Will produce when authentication needed:

POST https://host:5000/v3/auth/tokens HTTP/1.1
{
    "auth": {
        "identity": {
            "methods": ["password"],
            "password": {
                "user": {
                    "name": "foo",
                    "domain": {
                        "name": "ldap"
                    },
                    "password": "bar"
                }
            }
        },
        "scope": {
            "project": {
                "name": "myTenant",
                "domain": {
                    "name": "ldap"
                }
            }
        }
    }
}

If the project domain is different than the user domain (Use case when 'default' is used for projects and a third-part IAM like ldap is use for user authentication), use this snippet:

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "3");
overrides.put(KeystoneProperties.SCOPE, "project:myTenant");
overrides.put(KeystoneProperties.PROJECT_DOMAIN_NAME, "default"); // Since jclouds > v2.1.0 (see PROJECT_DOMAIN_ID as complement)

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v3")
   .credentials("ldap:foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

Will produce when authentication needed:

POST https://host:5000/v3/auth/tokens HTTP/1.1
{
    "auth": {
        "identity": {
            "methods": ["password"],
            "password": {
                "user": {
                    "name": "foo",
                    "domain": {
                        "name": "ldap"
                    },
                    "password": "bar"
                }
            }
        },
        "scope": {
            "project": {
                "name": "myTenant",
                "domain": {
                    "name": "default"
                }
            }
        }
    }
}

v3: Domain-scoped

If your authentication is domain-scoped, this snippet:

final Properties overrides = new Properties();
overrides.put(KeystoneProperties.KEYSTONE_VERSION, "3");
overrides.put(KeystoneProperties.SCOPE, "domain:default");

ContextBuilder.newBuilder("openstack-nova")
   .endpoint("https://host:5000/v3")
   .credentials("ldap:foo", "bar")
   .overrides(overrides)
   .buildApi(NovaApi.class);

Will produce when authentication needed:

POST https://host:5000/v3/auth/tokens HTTP/1.1
{
    "auth": {
        "identity": {
            "methods": ["password"],
            "password": {
                "user": {
                    "name": "foo",
                    "domain": {
                        "name": "ldap"
                    },
                    "password": "bar"
                }
            }
        },
        "scope": {
            "domain": {
                "name": "default"
            }
        }
    }
}

Nova: List Servers

Introduction

OpenStack Compute (aka Nova) is an easy to use service that provides on-demand servers that you can use to to build dynamic websites, deliver mobile apps, or crunch big data.

The Source Code

  1. Create a Java source file called JCloudsNova.java in the jclouds directory above.
  2. You should now have a directory with the following structure:
    • jclouds/
      • JCloudsNova.java
      • pom.xml
      • lib/
        • *.jar
  3. Open JCloudsNova.java for editing, read the code below, and copy it in.
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Closeables;
import com.google.inject.Module;
import org.jclouds.ContextBuilder;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.domain.Server;
import org.jclouds.openstack.nova.v2_0.features.ServerApi;

import java.io.Closeable;
import java.io.IOException;
import java.util.Set;

public class JCloudsNova implements Closeable {
    private final NovaApi novaApi;
    private final Set<String> regions;

    public static void main(String[] args) throws IOException {
        JCloudsNova jcloudsNova = new JCloudsNova();

        try {
            jcloudsNova.listServers();
            jcloudsNova.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jcloudsNova.close();
        }
    }

    public JCloudsNova() {
        Iterable<Module> modules = ImmutableSet.<Module>of(new SLF4JLoggingModule());

        // Please refer to 'Keystone v2-v3 authentication' section for complete authentication use case
        String provider = "openstack-nova";
        String identity = "demo:demo"; // tenantName:userName
        String credential = "devstack";

        novaApi = ContextBuilder.newBuilder(provider)
                .endpoint("http://xxx.xxx.xxx.xxx:5000/v2.0/")
                .credentials(identity, credential)
                .modules(modules)
                .buildApi(NovaApi.class);
        regions = novaApi.getConfiguredRegions();
    }

    private void listServers() {
        for (String region : regions) {
            ServerApi serverApi = novaApi.getServerApi(region);

            System.out.println("Servers in " + region);

            for (Server server : serverApi.listInDetail().concat()) {
                System.out.println("  " + server);
            }
        }
    }

    public void close() throws IOException {
        Closeables.close(novaApi, true);
    }
}

In the constructor note that

Compile and Run

$ javac -classpath ".:lib/*" JCloudsNova.java

$ java -classpath ".:lib/*" JCloudsNova

Servers in RegionOne
  Server{id=...}
  ...

# You'll see a lot of logging in the output

Swift: Use Containers

Introduction

OpenStack Object Storage (aka Swift) provides redundant, scalable object storage using clusters of standardized servers capable of storing petabytes of data.

The Source Code

  1. Create a Java source file called JCloudsSwift.java in the jclouds directory above.
  2. You should now have a directory with the following structure:
    • jclouds/
      • JCloudsSwift.java
      • pom.xml
      • lib/
        • *.jar
  3. Open JCloudsSwift.java for editing, read the code below, and copy it in.
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Closeables;
import com.google.inject.Module;
import org.jclouds.ContextBuilder;
import org.jclouds.io.Payload;
import org.jclouds.logging.slf4j.config.SLF4JLoggingModule;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.Container;
import org.jclouds.openstack.swift.v1.features.ContainerApi;
import org.jclouds.openstack.swift.v1.features.ObjectApi;
import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
import org.jclouds.openstack.swift.v1.options.PutOptions;

import java.io.Closeable;
import java.io.IOException;
import java.util.Set;

import static com.google.common.io.ByteSource.wrap;
import static org.jclouds.io.Payloads.newByteSourcePayload;

public class JCloudsSwift implements Closeable {
   public static final String CONTAINER_NAME = "jclouds-example";
   public static final String OBJECT_NAME = "jclouds-example.txt";

   private SwiftApi swiftApi;

   public static void main(String[] args) throws IOException {
      JCloudsSwift jcloudsSwift = new JCloudsSwift();

      try {
         jcloudsSwift.createContainer();
         jcloudsSwift.uploadObjectFromString();
         jcloudsSwift.listContainers();
         jcloudsSwift.close();
      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         jcloudsSwift.close();
      }
   }

   public JCloudsSwift() {
      Iterable<Module> modules = ImmutableSet.<Module>of(
            new SLF4JLoggingModule());

      // Please refer to 'Keystone v2-v3 authentication' section for complete authentication use case
      String provider = "openstack-swift";
      String identity = "demo:demo"; // tenantName:userName
      String credential = "devstack";

      swiftApi = ContextBuilder.newBuilder(provider)
            .endpoint("http://xxx.xxx.xxx.xxx:5000/v2.0/")
            .credentials(identity, credential)
            .modules(modules)
            .buildApi(SwiftApi.class);
   }

   private void createContainer() {
      System.out.println("Create Container");

      ContainerApi containerApi = swiftApi.getContainerApi("RegionOne");
      CreateContainerOptions options = CreateContainerOptions.Builder
            .metadata(ImmutableMap.of(
                  "key1", "value1",
                  "key2", "value2"));

      containerApi.create(CONTAINER_NAME, options);

      System.out.println("  " + CONTAINER_NAME);
   }

   private void uploadObjectFromString() {
      System.out.println("Upload Object From String");

      ObjectApi objectApi = swiftApi.getObjectApi("RegionOne", CONTAINER_NAME);
      Payload payload = newByteSourcePayload(wrap("Hello World".getBytes()));

      objectApi.put(OBJECT_NAME, payload, PutOptions.Builder.metadata(ImmutableMap.of("key1", "value1")));

      System.out.println("  " + OBJECT_NAME);
   }

   private void listContainers() {
      System.out.println("List Containers");

      ContainerApi containerApi = swiftApi.getContainerApi("RegionOne");
      Set<Container> containers = containerApi.list().toSet();

      for (Container container : containers) {
         System.out.println("  " + container);
      }
   }

   public void close() throws IOException {
      Closeables.close(swiftApi, true);
   }
}

Compile and Run

$ javac -classpath ".:lib/*" JCloudsSwift.java

$ java -classpath ".:lib/*" JCloudsSwift

Create Container
  jclouds-example
Upload Object From String
  jclouds-example.txt
List Containers
  Container{name=...}
  ...

# You'll see a lot of logging in the output

Next Steps

import org.jclouds.Constants;
import org.jclouds.openstack.keystone.v2_0.config.CredentialTypes;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;

// snip

public JCloudsNova() {
    Iterable<Module> modules = ImmutableSet.<Module>of(new SLF4JLoggingModule());

    Properties overrides = new Properties();
    overrides.setProperty(KeystoneProperties.CREDENTIAL_TYPE, CredentialTypes.PASSWORD_CREDENTIALS);
    overrides.setProperty(Constants.PROPERTY_API_VERSION, "2");

    // Please refer to 'Keystone v2-v3 authentication' section for complete authentication use case
    String provider = "openstack-nova";
    String identity = "username";
    String credential = "password";

    novaApi = ContextBuilder.newBuilder(provider)
            .endpoint("https://identity.api.rackspacecloud.com/v2.0/")
            .credentials(identity, credential)
            .modules(modules)
            .overrides(overrides)
            .buildApi(NovaApi.class);
    regions = novaApi.getConfiguredRegions();
}

OpenStack Dependencies

This pom.xml file specifies all of the dependencies you'll need to work with OpenStack.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <jclouds.version>2.5.0</jclouds.version>
  </properties>
  <groupId>org.apache.jclouds.examples</groupId>
  <artifactId>openstack-examples</artifactId>
  <version>1.0</version>
  <dependencies>
    <!-- jclouds dependencies -->
    <dependency>
      <groupId>org.apache.jclouds.driver</groupId>
      <artifactId>jclouds-slf4j</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.driver</groupId>
      <artifactId>jclouds-sshj</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <!-- jclouds OpenStack dependencies -->
    <dependency>
      <groupId>org.apache.jclouds.api</groupId>
      <artifactId>openstack-keystone</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.api</groupId>
      <artifactId>openstack-nova</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.api</groupId>
      <artifactId>openstack-swift</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.api</groupId>
      <artifactId>openstack-cinder</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.api</groupId>
      <artifactId>openstack-trove</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.labs</groupId>
      <artifactId>openstack-glance</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.labs</groupId>
      <artifactId>openstack-marconi</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.jclouds.labs</groupId>
      <artifactId>openstack-neutron</artifactId>
      <version>${jclouds.version}</version>
    </dependency>
    <!-- 3rd party dependencies -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.0.13</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.25</version>
    </dependency>
  </dependencies>
</project>