diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java
new file mode 100644
index 0000000000..47812eb136
--- /dev/null
+++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+package org.jclouds.openstack.nova.v2_0.predicates;
+
+import com.google.common.base.Predicate;
+import org.jclouds.openstack.nova.v2_0.domain.Server;
+import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
+import org.jclouds.openstack.nova.v2_0.features.ServerApi;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status;
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.SHUTOFF;
+import static org.jclouds.util.Predicates2.retry;
+
+/**
+ * This class tests to see if a Server or ServerCreated has reached a desired status. This class is most useful when
+ * paired with a RetryablePredicate as in the code below. Together these classes can be used to block execution until
+ * the Server or ServerCreated has reached that desired status. This is useful when your Server needs to be 100% ready
+ * before you can continue with execution.
+ *
+ * For example, you can use the factory methods like so.
+ *
+ *
+ * {@code
+ * ServerCreated serverCreated = serverApi.create("my-server", image.getId(), flavor.getId());
+ *
+ * if (!ServerPredicates.awaitActive(serverApi).apply(serverCreated.getId())) {
+ * throw new TimeoutException("Timeout on server: " + serverCreated);
+ * }
+ *
+ *
+ *
+ * {@code
+ * if (!ServerPredicates.awaitStatus(serverApi, ACTIVE, 300, 2).apply(server.getId())) {
+ * throw new TimeoutException("Timeout on server: " + serverCreated);
+ * }
+ *
+ */
+public class ServerPredicates {
+ private static final int TEN_MINUTES = 600;
+ private static final int FIVE_SECONDS = 5;
+
+ /**
+ * Waits until a Server is ACTIVE.
+ *
+ * @param serverApi The ServerApi in the zone where your Server resides.
+ * @return Predicate that will check the status every 5 seconds for a maximum of 10 minutes.
+ */
+ public static Predicate awaitActive(ServerApi serverApi) {
+ return awaitStatus(serverApi, ACTIVE, TEN_MINUTES, FIVE_SECONDS);
+ }
+
+ /**
+ * Waits until a Server is SHUTOFF.
+ *
+ * @param serverApi The ServerApi in the zone where your Server resides.
+ * @return Predicate that will check the status every 5 seconds for a maximum of 10 minutes.
+ */
+ public static Predicate awaitShutoff(ServerApi serverApi) {
+ return awaitStatus(serverApi, SHUTOFF, TEN_MINUTES, FIVE_SECONDS);
+ }
+
+ /**
+ * Waits until a Server reaches Status.
+ *
+ * @param serverApi The ServerApi in the zone where your Server resides.
+ * @return Predicate that will check the status every periodInSec seconds for a maximum of maxWaitInSec minutes.
+ */
+ public static Predicate awaitStatus(
+ ServerApi serverApi, Status status, long maxWaitInSec, long periodInSec) {
+ ServerStatusPredicate statusPredicate = new ServerStatusPredicate(serverApi, status);
+
+ return retry(statusPredicate, maxWaitInSec, periodInSec, periodInSec, SECONDS);
+ }
+
+ public static class ServerStatusPredicate implements Predicate {
+ private final ServerApi serverApi;
+ private final Status status;
+
+ public ServerStatusPredicate(ServerApi serverApi, Status status) {
+ this.serverApi = checkNotNull(serverApi, "serverApi must be defined");
+ this.status = checkNotNull(status, "status must be defined");
+ }
+
+ /**
+ * @return boolean Return true when the Server reaches the Status, false otherwise
+ * @throws IllegalStateException if the Server associated with serverId does not exist
+ */
+ @Override
+ public boolean apply(String serverId) {
+ checkNotNull(serverId, "server must be defined");
+
+ Server server = serverApi.get(serverId);
+
+ if (server == null) {
+ throw new IllegalStateException(String.format("Server %s not found.", serverId));
+ }
+
+ return status.equals(server.getStatus());
+ }
+ }
+}
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
index 3553d1888b..7e406d11a9 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/features/ServerApiLiveTest.java
@@ -16,11 +16,14 @@
*/
package org.jclouds.openstack.nova.v2_0.features;
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
+import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitActive;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
+import org.jclouds.http.HttpResponseException;
import org.jclouds.openstack.nova.v2_0.domain.Network;
import org.jclouds.openstack.nova.v2_0.domain.Server;
import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
@@ -84,9 +87,9 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
for (String zoneId : zones) {
ServerApi serverApi = api.getServerApiForZone(zoneId);
try {
- serverId = createServer(zoneId, "nova", Server.Status.ACTIVE).getId();
+ serverId = createServer(zoneId, "nova").getId();
Server server = serverApi.get(serverId);
- assertEquals(server.getStatus(), Server.Status.ACTIVE);
+ assertEquals(server.getStatus(), ACTIVE);
} finally {
serverApi.delete(serverId);
}
@@ -112,9 +115,10 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
ServerCreated server = serverApi.create(hostName, imageIdForZone(zoneId), "1", options);
serverId = server.getId();
- blockUntilServerInState(server.getId(), serverApi, Server.Status.ACTIVE);
+ awaitActive(serverApi).apply(server.getId());
+
Server serverCheck = serverApi.get(serverId);
- assertEquals(serverCheck.getStatus(), Server.Status.ACTIVE);
+ assertEquals(serverCheck.getStatus(), ACTIVE);
} finally {
if (serverId != null) {
serverApi.delete(serverId);
@@ -129,9 +133,12 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
for (String zoneId : zones) {
ServerApi serverApi = api.getServerApiForZone(zoneId);
try {
- serverId = createServer(zoneId, "err", Server.Status.ERROR).getId();
- Server server = serverApi.get(serverId);
- assertEquals(server.getStatus(), Server.Status.ERROR);
+ serverId = createServer(zoneId, "err").getId();
+ } catch (HttpResponseException e) {
+ // Here is an implementation detail difference between OpenStack and some providers.
+ // Some providers accept a bad availability zone and create the server in the zoneId.
+ // Vanilla OpenStack will error out with a 400 Bad Request
+ assertEquals(e.getResponse().getStatusCode(), 400);
} finally {
serverApi.delete(serverId);
}
@@ -146,11 +153,11 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
for (String zoneId : zones) {
ServerApi serverApi = api.getServerApiForZone(zoneId);
try {
- serverId = createServer(zoneId, Server.Status.ACTIVE).getId();
+ serverId = createServer(zoneId, null).getId();
Server server = serverApi.get(serverId);
- assertEquals(server.getStatus(), Server.Status.ACTIVE);
+ assertEquals(server.getStatus(), ACTIVE);
RebuildServerOptions options = new RebuildServerOptions().
withImage(server.getImage().getId()).
@@ -173,22 +180,18 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
}
}
- private Server createServer(String regionId, Server.Status serverStatus) {
- ServerApi serverApi = api.getServerApiForZone(regionId);
+ private Server createServer(String zoneId, String availabilityZoneId) {
+ ServerApi serverApi = api.getServerApiForZone(zoneId);
+
CreateServerOptions options = new CreateServerOptions();
- ServerCreated server = serverApi.create(hostName, imageIdForZone(regionId), flavorRefForZone(regionId), options);
+ if (availabilityZoneId != null) {
+ options = options.availabilityZone(availabilityZoneId);
+ }
- blockUntilServerInState(server.getId(), serverApi, serverStatus);
+ ServerCreated server = serverApi.create(hostName, imageIdForZone(zoneId), flavorRefForZone(zoneId), options);
- return serverApi.get(server.getId());
- }
+ awaitActive(serverApi).apply(server.getId());
- private Server createServer(String regionId, String availabilityZoneId, Server.Status serverStatus) {
- ServerApi serverApi = api.getServerApiForZone(regionId);
- CreateServerOptions options = new CreateServerOptions();
- options = options.availabilityZone(availabilityZoneId);
- ServerCreated server = serverApi.create(hostName, imageIdForZone(regionId), flavorRefForZone(regionId), options);
- blockUntilServerInState(server.getId(), serverApi, serverStatus);
return serverApi.get(server.getId());
}
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java
index da1847bbe2..fd8ad6fd9d 100644
--- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/internal/BaseNovaApiLiveTest.java
@@ -19,6 +19,7 @@ package org.jclouds.openstack.nova.v2_0.internal;
import java.util.Properties;
import java.util.Set;
+import com.google.common.collect.*;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
import org.jclouds.openstack.nova.v2_0.NovaApi;
@@ -35,9 +36,6 @@ import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Throwables;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Ordering;
/**
* Tests behavior of {@code NovaApi}
@@ -58,7 +56,15 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest {
@Override
public void setup() {
super.setup();
- zones = api.getConfiguredZones();
+
+ String testZone = System.getProperty("test." + provider + ".zone");
+
+ if (testZone != null) {
+ zones = ImmutableSet.of(testZone);
+ } else {
+ zones = api.getConfiguredZones();
+ }
+
for (String zone : zones) {
ServerApi serverApi = api.getServerApiForZone(zone);
for (Resource server : serverApi.list().concat()) {
@@ -83,7 +89,7 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest {
return serverApi.get(server.getId());
}
- /**
+ /**
* Will block until the requested server is in the correct state, if Extended Server Status extension is loaded
* this will continue to block while any task is in progress.
*/
@@ -100,10 +106,12 @@ public class BaseNovaApiLiveTest extends BaseApiLiveTest {
}
}
}
-
+
protected String imageIdForZone(String zoneId) {
ImageApi imageApi = api.getImageApiForZone(zoneId);
- return Iterables.getLast(imageApi.list().concat()).getId();
+
+ // Get the first image from the list as it tends to be "lighter" and faster to start
+ return Iterables.get(imageApi.list().concat(), 0).getId();
}
protected String flavorRefForZone(String zoneId) {
diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicatesMockTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicatesMockTest.java
new file mode 100644
index 0000000000..4498c7c43f
--- /dev/null
+++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicatesMockTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+package org.jclouds.openstack.nova.v2_0.predicates;
+
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import org.jclouds.openstack.nova.v2_0.NovaApi;
+import org.jclouds.openstack.nova.v2_0.features.ServerApi;
+import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
+import org.testng.annotations.Test;
+
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
+import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.SHUTOFF;
+import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.*;
+import static org.testng.Assert.*;
+
+@Test(groups = "unit", testName = "ServerPredicatesMockTest")
+public class ServerPredicatesMockTest extends BaseOpenStackMockTest {
+ public void testAwaitActive() throws Exception {
+ MockWebServer server = mockOpenStackServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ String serverDetailsActive = stringFromResource("/server_details.json").replace("BUILD(scheduling)", ACTIVE.value());
+ server.enqueue(new MockResponse().setBody(serverDetailsActive));
+
+ try {
+ NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
+ ServerApi serverApi = novaApi.getServerApiForZone(("RegionOne"));
+
+ boolean result = awaitActive(serverApi).apply("52415800-8b69-11e0-9b19-734f000004d2");
+
+ assertTrue(result);
+ assertEquals(server.getRequestCount(), 5);
+ assertAuthentication(server);
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testAwaitShutoff() throws Exception {
+ MockWebServer server = mockOpenStackServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ String serverDetailsShutoff = stringFromResource("/server_details.json").replace("BUILD(scheduling)", SHUTOFF.value());
+ server.enqueue(new MockResponse().setBody(serverDetailsShutoff));
+
+ try {
+ NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
+ ServerApi serverApi = novaApi.getServerApiForZone(("RegionOne"));
+
+ boolean result = awaitShutoff(serverApi).apply("52415800-8b69-11e0-9b19-734f000004d2");
+
+ assertTrue(result);
+ assertEquals(server.getRequestCount(), 7);
+ assertAuthentication(server);
+ } finally {
+ server.shutdown();
+ }
+ }
+
+ public void testAwaitTimeout() throws Exception {
+ MockWebServer server = mockOpenStackServer();
+ server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
+
+ for (int i=0; i < 20; i++) {
+ server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
+ }
+
+ try {
+ NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
+ ServerApi serverApi = novaApi.getServerApiForZone(("RegionOne"));
+
+ boolean result = awaitStatus(serverApi, ACTIVE, 3, 1).apply("52415800-8b69-11e0-9b19-734f000004d2");
+
+ assertFalse(result);
+ assertAuthentication(server);
+ } finally {
+ server.shutdown();
+ }
+ }
+}
diff --git a/apis/openstack-nova/src/test/resources/access.json b/apis/openstack-nova/src/test/resources/access.json
new file mode 100644
index 0000000000..84a962531a
--- /dev/null
+++ b/apis/openstack-nova/src/test/resources/access.json
@@ -0,0 +1,228 @@
+{
+ "access": {
+ "metadata": {
+ "roles": [
+ "9fe2ff9ee4384b1894a90878d3e92bab",
+ "b926cb0f4e2642678735f86c2b06205e",
+ "33484487e73d4da0918a19b9c7e1f8ae",
+ "f2e54c2105fb49e29479af047115cebc"
+ ],
+ "is_admin": 0
+ },
+ "user": {
+ "name":"joe",
+ "roles": [
+ {
+ "name":"_member_"
+ },
+ {
+ "name":"anotherrole"
+ },
+ {
+ "name":"heat_stack_owner"
+ },
+ {
+ "name":"Member"
+ }
+ ],
+ "id":"8fbf8e68d36e4ac7bcf912a26213bd49",
+ "roles_links": [],
+ "username":"joe"
+ },
+ "serviceCatalog": [
+ {
+ "name":"nova",
+ "type":"compute",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v2/da0d12be20394afb851716e10a49e4a7",
+ "id":"2122bcaa704343c19ad2578410d4961d",
+ "internalURL":"URL/v2/da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL/v2/da0d12be20394afb851716e10a49e4a7"
+ }
+ ]
+ },
+ {
+ "name":"neutron",
+ "type":"network",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/",
+ "id":"65a4d3f13cfb49a6a57a04e205cc2158",
+ "internalURL":"URL/",
+ "region":"RegionOne",
+ "adminURL":"URL/"
+ }
+ ]
+ },
+ {
+ "name":"cinderv2",
+ "type":"volumev2",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v2/da0d12be20394afb851716e10a49e4a7",
+ "id":"31fe4d92eac44044b05be21c6f44cebc",
+ "internalURL":"URL/v2/da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL/v2/da0d12be20394afb851716e10a49e4a7"
+ }
+ ]
+ },
+ {
+ "name":"trove",
+ "type":"database",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v1.0/da0d12be20394afb851716e10a49e4a7",
+ "id":"06b7a7dbd25c4a01819c879700a9712a",
+ "internalURL":"URL/v1.0/da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL/v1.0/da0d12be20394afb851716e10a49e4a7"
+ }
+ ]
+ },
+ {
+ "name":"s3",
+ "type":"s3",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL",
+ "id":"93b0b67091324e8ba01b62ee0584994c",
+ "internalURL":"URL",
+ "region":"RegionOne",
+ "adminURL":"URL"
+ }
+ ]
+ },
+ {
+ "name":"glance",
+ "type":"image",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL",
+ "id":"a542e91bcfa046bfa1bf2397356d1414",
+ "internalURL":"URL",
+ "region":"RegionOne",
+ "adminURL":"URL"
+ }
+ ]
+ },
+ {
+ "name":"novav3",
+ "type":"computev3",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v3",
+ "id":"9c3e8abb576d483db93bcef70c67bc1d",
+ "internalURL":"URL/v3",
+ "region":"RegionOne",
+ "adminURL":"URL/v3"
+ }
+ ]
+ },
+ {
+ "name":"heat",
+ "type":"cloudformation",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v1",
+ "id":"6f4ca5ca9698425b85c300b3fc176c39",
+ "internalURL":"URL/v1",
+ "region":"RegionOne",
+ "adminURL":"URL/v1"
+ }
+ ]
+ },
+ {
+ "name":"cinder",
+ "type":"volume",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v1/da0d12be20394afb851716e10a49e4a7",
+ "id":"037039c676694a35aa28d34fce09e51d",
+ "internalURL":"URL/v1/da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL/v1/da0d12be20394afb851716e10a49e4a7"
+ }
+ ]
+ },
+ {
+ "name":"ec2",
+ "type":"ec2",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/services/Cloud",
+ "id":"1d242631bccb4ff4ba7a395dbcb51648",
+ "internalURL":"URL/services/Cloud",
+ "region":"RegionOne",
+ "adminURL":"URL/services/Admin"
+ }
+ ]
+ },
+ {
+ "name":"heat",
+ "type":"orchestration",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v1/da0d12be20394afb851716e10a49e4a7",
+ "id":"199d00075e4a40308a6ad2aa8980d0cd",
+ "internalURL":"URL/v1/da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL/v1/da0d12be20394afb851716e10a49e4a7"
+ }
+ ]
+ },
+ {
+ "name":"swift",
+ "type":"object-store",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v1/AUTH_da0d12be20394afb851716e10a49e4a7",
+ "id":"26b2cb1efb044193b847fc3f2fb12e82",
+ "internalURL":"URL/v1/AUTH_da0d12be20394afb851716e10a49e4a7",
+ "region":"RegionOne",
+ "adminURL":"URL"
+ }
+ ]
+ },
+ {
+ "name":"keystone",
+ "type":"identity",
+ "endpoints_links": [],
+ "endpoints": [
+ {
+ "publicURL":"URL/v2.0",
+ "id":"1bbfe80b50df4c4a84040aa782e42140",
+ "internalURL":"URL/v2.0",
+ "region":"RegionOne",
+ "adminURL":"URL/v2.0"
+ }
+ ]
+ }
+ ],
+ "token": {
+ "tenant": {
+ "name":"jclouds",
+ "id":"da0d12be20394afb851716e10a49e4a7",
+ "enabled": true,
+ "description": null
+ },
+ "id":"TOKEN",
+ "expires":"2014-04-28T22:48:24Z",
+ "issued_at":"2014-04-28T21:48:24.972896"
+ }
+ }
+}
\ No newline at end of file