Added the jclouds block storage sample
Added the block storage code sample for jclouds and modified the block storage chapter to reference it. Change-Id: I299b47c27a6527399756f289dc4447090d6bc156
This commit is contained in:
parent
3994c38674
commit
6276f322d8
243
firstapp/samples/jclouds/BlockStorage.java
Normal file
243
firstapp/samples/jclouds/BlockStorage.java
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.google.common.io.Closeables;
|
||||
import org.jclouds.ContextBuilder;
|
||||
import org.jclouds.net.domain.IpProtocol;
|
||||
import org.jclouds.openstack.cinder.v1.CinderApi;
|
||||
import org.jclouds.openstack.cinder.v1.domain.Snapshot;
|
||||
import org.jclouds.openstack.cinder.v1.domain.Volume;
|
||||
import org.jclouds.openstack.cinder.v1.features.SnapshotApi;
|
||||
import org.jclouds.openstack.cinder.v1.features.VolumeApi;
|
||||
import org.jclouds.openstack.cinder.v1.options.CreateSnapshotOptions;
|
||||
import org.jclouds.openstack.cinder.v1.options.CreateVolumeOptions;
|
||||
import org.jclouds.openstack.cinder.v1.predicates.SnapshotPredicates;
|
||||
import org.jclouds.openstack.cinder.v1.predicates.VolumePredicates;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Ingress;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.ServerApi;
|
||||
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static java.lang.System.out;
|
||||
|
||||
|
||||
public class BlockStorage implements Closeable {
|
||||
|
||||
// Set the following to match the values for your cloud
|
||||
private static final String IDENTITY = "your_project_name:your_auth_username"; // note: projectName:userName
|
||||
private static final String KEY_PAIR_NAME = "your_key_pair_name";
|
||||
private static final String AUTH_URL = "http://controller:5000";
|
||||
private static final String AVAILABILITY_ZONE = "your_availability_zone";
|
||||
private static final String IMAGE_ID = "your_desired_image_id";
|
||||
private static final String FLAVOR_ID = "your_desired_flavor_id";
|
||||
|
||||
private static final String DATABASE_SECURITY_GROUP_NAME = "database";
|
||||
|
||||
private final CinderApi cinderApi;
|
||||
private final VolumeApi volumeApi;
|
||||
private final NovaApi novaApi;
|
||||
private final ServerApi serverApi;
|
||||
private final String region;
|
||||
|
||||
// step-1
|
||||
public BlockStorage(final String password) {
|
||||
cinderApi = ContextBuilder.newBuilder("openstack-cinder")
|
||||
.endpoint(AUTH_URL)
|
||||
.credentials(IDENTITY, password)
|
||||
.buildApi(CinderApi.class);
|
||||
region = cinderApi.getConfiguredRegions().iterator().next();
|
||||
out.println("Running in region: " + region);
|
||||
volumeApi = cinderApi.getVolumeApi(region);
|
||||
novaApi = ContextBuilder.newBuilder("openstack-nova")
|
||||
.endpoint(AUTH_URL)
|
||||
.credentials(IDENTITY, password)
|
||||
.buildApi(NovaApi.class);
|
||||
serverApi = novaApi.getServerApi(region);
|
||||
}
|
||||
|
||||
// step-2
|
||||
private Volume createVolume() throws TimeoutException {
|
||||
String volumeName = "Test";
|
||||
CreateVolumeOptions options = CreateVolumeOptions.Builder
|
||||
.name(volumeName)
|
||||
.availabilityZone(AVAILABILITY_ZONE);
|
||||
out.println("Creating 1 Gig volume named '" + volumeName + "'");
|
||||
Volume volume = volumeApi.create(1, options);
|
||||
// Wait for the volume to become available
|
||||
if (!VolumePredicates.awaitAvailable(volumeApi).apply(volume)) {
|
||||
throw new TimeoutException("Timeout on volume create");
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
// step-3
|
||||
private void listVolumes() {
|
||||
out.println("Listing volumes");
|
||||
cinderApi.getConfiguredRegions().forEach((region) -> {
|
||||
out.println(" In region: " + region);
|
||||
cinderApi.getVolumeApi(region).list().forEach((volume) -> {
|
||||
out.println(" " + volume.getName());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// step-4
|
||||
private boolean isSecurityGroup(String securityGroupName, SecurityGroupApi securityGroupApi) {
|
||||
for (SecurityGroup securityGroup : securityGroupApi.list()) {
|
||||
if (securityGroup.getName().equals(securityGroupName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// A utility method to convert a google optional into a Java 8 optional
|
||||
private <T> Optional<T> optional(com.google.common.base.Optional<T> target) {
|
||||
return target.isPresent() ? Optional.of(target.get()) : Optional.empty();
|
||||
}
|
||||
|
||||
private void createSecurityGroup(String securityGroupName) {
|
||||
optional(novaApi.getSecurityGroupApi(region)).ifPresent(securityGroupApi -> {
|
||||
if (isSecurityGroup(securityGroupName, securityGroupApi)) {
|
||||
out.println("Security group " + securityGroupName + " already exists");
|
||||
} else {
|
||||
out.println("Creating security group " + securityGroupName + "...");
|
||||
SecurityGroup securityGroup =
|
||||
securityGroupApi.createWithDescription(securityGroupName,
|
||||
"For database service");
|
||||
securityGroupApi.createRuleAllowingCidrBlock(
|
||||
securityGroup.getId(), Ingress
|
||||
.builder()
|
||||
.ipProtocol(IpProtocol.TCP)
|
||||
.fromPort(3306)
|
||||
.toPort(3306)
|
||||
.build(), "0.0.0.0/0");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String createDbInstance() throws TimeoutException {
|
||||
String instanceName = "app-database";
|
||||
out.println("Creating instance " + instanceName);
|
||||
CreateServerOptions allInOneOptions = CreateServerOptions.Builder
|
||||
.keyPairName(KEY_PAIR_NAME)
|
||||
.availabilityZone(AVAILABILITY_ZONE)
|
||||
.securityGroupNames(DATABASE_SECURITY_GROUP_NAME);
|
||||
ServerCreated server = serverApi.create(instanceName, IMAGE_ID, FLAVOR_ID, allInOneOptions);
|
||||
String id = server.getId();
|
||||
// Wait for the server to become available
|
||||
if (!ServerPredicates.awaitActive(serverApi).apply(id)) {
|
||||
throw new TimeoutException("Timeout on server create");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// step-5
|
||||
private void attachVolume(Volume volume, String instanceId) throws TimeoutException {
|
||||
out.format("Attaching volume %s to instance %s%n", volume.getId(), instanceId);
|
||||
optional(novaApi.getVolumeAttachmentApi(region)).ifPresent(volumeAttachmentApi -> {
|
||||
volumeAttachmentApi.attachVolumeToServerAsDevice(volume.getId(), instanceId, "/dev/vdb");
|
||||
}
|
||||
);
|
||||
// Wait for the volume to be attached
|
||||
if (!VolumePredicates.awaitInUse(volumeApi).apply(volume)) {
|
||||
throw new TimeoutException("Timeout on volume attach");
|
||||
}
|
||||
}
|
||||
|
||||
// step-6
|
||||
private void detachVolume(Volume volume, String instanceId) throws TimeoutException {
|
||||
out.format("Detach volume %s from instance %s%n", volume.getId(), instanceId);
|
||||
optional(novaApi.getVolumeAttachmentApi(region)).ifPresent(volumeAttachmentApi -> {
|
||||
volumeAttachmentApi.detachVolumeFromServer(volume.getId(), instanceId);
|
||||
});
|
||||
// Wait for the volume to be detached
|
||||
if (!VolumePredicates.awaitAvailable(volumeApi).apply(Volume.forId(volume.getId()))) {
|
||||
throw new TimeoutException("Timeout on volume detach");
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyVolume(Volume volume) throws TimeoutException {
|
||||
out.println("Destroy volume " + volume.getName());
|
||||
volumeApi.delete(volume.getId());
|
||||
// Wait for the volume to be deleted
|
||||
if (!VolumePredicates.awaitDeleted(volumeApi).apply(volume)) {
|
||||
throw new TimeoutException("Timeout on volume delete");
|
||||
}
|
||||
}
|
||||
|
||||
// step-7
|
||||
private Snapshot createVolumeSnapshot(Volume volume) throws TimeoutException {
|
||||
out.println("Create snapshot of volume " + volume.getName());
|
||||
SnapshotApi snapshotApi = cinderApi.getSnapshotApi(region);
|
||||
CreateSnapshotOptions options = CreateSnapshotOptions.Builder
|
||||
.name(volume.getName() + " snapshot")
|
||||
.description("Snapshot of " + volume.getId());
|
||||
Snapshot snapshot = snapshotApi.create(volume.getId(), options);
|
||||
// Wait for the snapshot to become available
|
||||
if (!SnapshotPredicates.awaitAvailable(snapshotApi).apply(snapshot)) {
|
||||
throw new TimeoutException("Timeout on volume snapshot");
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private void deleteVolumeSnapshot(Snapshot snapshot) throws TimeoutException {
|
||||
out.println("Delete volume snapshot " + snapshot.getName());
|
||||
SnapshotApi snapshotApi = cinderApi.getSnapshotApi(region);
|
||||
snapshotApi.delete(snapshot.getId());
|
||||
// Wait for the snapshot to be deleted
|
||||
if (!SnapshotPredicates.awaitDeleted(snapshotApi).apply(snapshot)) {
|
||||
throw new TimeoutException("Timeout on snapshot delete");
|
||||
}
|
||||
}
|
||||
// step-8
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Closeables.close(novaApi, true);
|
||||
Closeables.close(cinderApi, true);
|
||||
}
|
||||
|
||||
public static void main(String... args) throws TimeoutException, IOException {
|
||||
try (Scanner scanner = new Scanner(System.in, "UTF-8")) {
|
||||
out.println("Please enter your API password: ");
|
||||
String password = scanner.next();
|
||||
try (BlockStorage storage = new BlockStorage(password)) {
|
||||
Volume volume = storage.createVolume();
|
||||
storage.listVolumes();
|
||||
storage.createSecurityGroup(DATABASE_SECURITY_GROUP_NAME);
|
||||
String dbInstanceId = storage.createDbInstance();
|
||||
storage.attachVolume(volume, dbInstanceId);
|
||||
storage.detachVolume(volume, dbInstanceId);
|
||||
Snapshot snapshot = storage.createVolumeSnapshot(volume);
|
||||
// have to delete the snapshot before we can delete the volume...
|
||||
storage.deleteVolumeSnapshot(snapshot);
|
||||
storage.destroyVolume(volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -46,10 +46,6 @@ first, learn how to create and attach a Block Storage device.
|
||||
|
||||
.. warning:: This section has not yet been completed for the fog SDK.
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. warning:: This section has not yet been completed for the jclouds SDK.
|
||||
|
||||
.. only:: pkgcloud
|
||||
|
||||
.. warning:: This section has not yet been completed for the pkgcloud SDK.
|
||||
@ -73,6 +69,13 @@ Connect to the API endpoint:
|
||||
:start-after: step-1
|
||||
:end-before: step-2
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-1
|
||||
:end-before: step-2
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -80,7 +83,7 @@ Connect to the API endpoint:
|
||||
:start-after: step-1
|
||||
:end-before: step-2
|
||||
|
||||
To try it out, make a 1GB volume called :test'.
|
||||
To try it out, make a 1GB volume called 'test'.
|
||||
|
||||
.. only:: libcloud
|
||||
|
||||
@ -93,6 +96,13 @@ To try it out, make a 1GB volume called :test'.
|
||||
|
||||
<StorageVolume id=755ab026-b5f2-4f53-b34a-6d082fb36689 size=1 driver=OpenStack>
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-2
|
||||
:end-before: step-3
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -100,7 +110,7 @@ To try it out, make a 1GB volume called :test'.
|
||||
:start-after: step-2
|
||||
:end-before: step-3
|
||||
|
||||
.. note:: The parameter :code:`size` is in gigabytes.
|
||||
.. note:: The parameter :code:`size` is in gigabytes.
|
||||
|
||||
To see if the volume creation was successful, list all volumes:
|
||||
|
||||
@ -115,6 +125,13 @@ To see if the volume creation was successful, list all volumes:
|
||||
|
||||
[<StorageVolume id=755ab026-b5f2-4f53-b34a-6d082fb36689 size=1 driver=OpenStack>]
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-3
|
||||
:end-before: step-4
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -141,6 +158,13 @@ MySQL, port 3306) from the network:
|
||||
:start-after: step-4
|
||||
:end-before: step-5
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-4
|
||||
:end-before: step-5
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -159,6 +183,13 @@ attach the volume to it at :code:`/dev/vdb`:
|
||||
:start-after: step-5
|
||||
:end-before: step-6
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-5
|
||||
:end-before: step-6
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -235,6 +266,13 @@ To detach and delete a volume:
|
||||
.. note:: :code:`detach_volume` and :code:`destroy_volume` take a
|
||||
volume object, not a name.
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-6
|
||||
:end-before: step-7
|
||||
|
||||
.. only:: shade
|
||||
|
||||
.. literalinclude:: ../samples/shade/block_storage.py
|
||||
@ -257,6 +295,22 @@ To detach and delete a volume:
|
||||
For information about these and other calls, see
|
||||
`libcloud documentation <http://ci.apache.org/projects/libcloud/docs/compute/drivers/openstack.html>`_.
|
||||
|
||||
.. only:: jclouds
|
||||
|
||||
Other features, such as creating volume snapshots, are useful for backups:
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
:start-after: step-7
|
||||
:end-before: step-8
|
||||
|
||||
The following file contains all of the code from this section of the
|
||||
tutorial. This comprehensive code sample lets you view and run the code
|
||||
as a single file.
|
||||
|
||||
.. literalinclude:: ../samples/jclouds/BlockStorage.java
|
||||
:language: java
|
||||
|
||||
Work with the OpenStack Database service
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user