diff --git a/labs/dynect/pom.xml b/labs/dynect/pom.xml
new file mode 100644
index 0000000000..d57ae5451c
--- /dev/null
+++ b/labs/dynect/pom.xml
@@ -0,0 +1,109 @@
+
+
+
+ 4.0.0
+
+ org.jclouds
+ jclouds-project
+ 1.6.0-SNAPSHOT
+ ../../project/pom.xml
+
+ org.jclouds.labs
+ dynect
+ jcloud dynect api
+ jclouds components for DynECT Managed DNS
+ bundle
+
+
+ https://api2.dynect.net/REST
+ 3.3.7
+
+
+
+
+ org.jclouds.dynect.v3*;version="${project.version}"
+
+ org.jclouds.rest.internal;version="${project.version}",
+ org.jclouds*;version="${project.version}",
+ *
+
+
+
+
+
+ org.jclouds
+ jclouds-core
+ ${project.version}
+
+
+ org.jclouds
+ jclouds-core
+ ${project.version}
+ test-jar
+ test
+
+
+ org.jclouds.driver
+ jclouds-slf4j
+ ${project.version}
+ test
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
+
+ live
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration
+ integration-test
+
+ test
+
+
+
+ ${test.dynect.endpoint}
+ ${test.dynect.api-version}
+ ${test.dynect.build-version}
+ ${test.dynect.identity}
+ ${test.dynect.credential}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java
new file mode 100644
index 0000000000..82d56f0b8e
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApi.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import org.jclouds.dynect.v3.features.SessionApi;
+import org.jclouds.rest.annotations.Delegate;
+
+/**
+ * Provides access to DynECT Managed DNS through the API2 api
+ *
+ *
+ * @see DynECTAsyncApi
+ * @see
+ * @author Adrian Cole
+ */
+public interface DynECTApi {
+
+ /**
+ * Provides synchronous access to Session features.
+ */
+ @Delegate
+ SessionApi getSessionApi();
+}
\ No newline at end of file
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApiMetadata.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApiMetadata.java
new file mode 100644
index 0000000000..d18c7949ab
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTApiMetadata.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.apis.ApiMetadata;
+import org.jclouds.dynect.v3.config.DynECTParserModule;
+import org.jclouds.dynect.v3.config.DynECTRestClientModule;
+import org.jclouds.rest.RestContext;
+import org.jclouds.rest.internal.BaseRestApiMetadata;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Module;
+
+/**
+ * Implementation of {@link ApiMetadata} for DynECT 1.0 API
+ *
+ * @author Adrian Cole
+ */
+public class DynECTApiMetadata extends BaseRestApiMetadata {
+
+ public static final String ANONYMOUS_IDENTITY = "ANONYMOUS";
+
+ public static final TypeToken> CONTEXT_TOKEN = new TypeToken>() {
+ private static final long serialVersionUID = 1L;
+ };
+
+ @Override
+ public Builder toBuilder() {
+ return new Builder().fromApiMetadata(this);
+ }
+
+ public DynECTApiMetadata() {
+ this(new Builder());
+ }
+
+ protected DynECTApiMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = BaseRestApiMetadata.defaultProperties();
+ properties.setProperty(PROPERTY_TIMEOUTS_PREFIX + "default", MINUTES.toMillis(3) + "");
+ return properties;
+ }
+
+ public static class Builder extends BaseRestApiMetadata.Builder {
+
+ protected Builder() {
+ super(DynECTApi.class, DynECTAsyncApi.class);
+ id("dynect")
+ .name("DynECT API2")
+ .identityName("Username (or " + ANONYMOUS_IDENTITY + " if anonymous)")
+ .defaultIdentity(ANONYMOUS_IDENTITY)
+ .credentialName("Password")
+ .defaultCredential(ANONYMOUS_IDENTITY)
+ .documentation(URI.create("https://manage.dynect.net/help/docs/api2/rest/"))
+ .version("3.3.7")
+ .defaultEndpoint("https://api2.dynect.net/REST")
+ .defaultProperties(DynECTApiMetadata.defaultProperties())
+ .defaultModules(ImmutableSet.>builder()
+ .add(DynECTParserModule.class)
+ .add(DynECTRestClientModule.class).build());
+ }
+
+ @Override
+ public DynECTApiMetadata build() {
+ return new DynECTApiMetadata(this);
+ }
+
+ @Override
+ protected Builder self() {
+ return this;
+ }
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java
new file mode 100644
index 0000000000..dd5c8e5e22
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTAsyncApi.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import javax.ws.rs.Produces;
+
+import org.jclouds.dynect.v3.features.SessionAsyncApi;
+import org.jclouds.rest.annotations.Delegate;
+import org.jclouds.rest.annotations.Headers;
+
+/**
+ * Provides access to DynECT Managed DNS through the API2 api
+ *
+ *
+ * @see DynECTApi
+ * @see
+ * @author Adrian Cole
+ */
+// required for all calls
+@Produces(APPLICATION_JSON)
+@Headers(keys = "API-Version", values = "{jclouds.api-version}")
+public interface DynECTAsyncApi {
+
+ /**
+ * Provides asynchronous access to Session features.
+ */
+ @Delegate
+ SessionAsyncApi getSessionApi();
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTFallbacks.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTFallbacks.java
new file mode 100644
index 0000000000..f6f9ab1794
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTFallbacks.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import static com.google.common.base.Predicates.equalTo;
+import static com.google.common.base.Throwables.propagate;
+import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static org.jclouds.http.HttpUtils.returnValueOnCodeOrNull;
+
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+public final class DynECTFallbacks {
+ private DynECTFallbacks() {
+ }
+
+ public static class FalseOn400 implements FutureFallback {
+ public ListenableFuture create(Throwable t) {
+ if (returnValueOnCodeOrNull(t, false, equalTo(400)) != null)
+ return immediateFuture(false);
+ throw propagate(t);
+ }
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTProviderMetadata.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTProviderMetadata.java
new file mode 100644
index 0000000000..ae1a1cca3e
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/DynECTProviderMetadata.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadata;
+
+/**
+ * Implementation of {@link org.jclouds.types.ProviderMetadata} for DynECT Managed DNS.
+ * @author Adrian Cole
+ */
+public class DynECTProviderMetadata extends BaseProviderMetadata {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public Builder toBuilder() {
+ return builder().fromProviderMetadata(this);
+ }
+
+ public DynECTProviderMetadata() {
+ super(builder());
+ }
+
+ public DynECTProviderMetadata(Builder builder) {
+ super(builder);
+ }
+
+ public static Properties defaultProperties() {
+ Properties properties = new Properties();
+ return properties;
+ }
+
+ public static class Builder extends BaseProviderMetadata.Builder {
+
+ protected Builder() {
+ id("dynect")
+ .name("DynECT Managed DNS")
+ .apiMetadata(new DynECTApiMetadata())
+ .homepage(URI.create("http://dyn.com/dns/dynect-managed-dns/"))
+ .console(URI.create("https://manage.dynect.net"))
+ .iso3166Codes("US-CA", "US-VA")
+ .endpoint("https://api2.dynect.net/REST")
+ .defaultProperties(DynECTProviderMetadata.defaultProperties());
+ }
+
+ @Override
+ public DynECTProviderMetadata build() {
+ return new DynECTProviderMetadata(this);
+ }
+
+ @Override
+ public Builder fromProviderMetadata(ProviderMetadata in) {
+ super.fromProviderMetadata(in);
+ return this;
+ }
+
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTParserModule.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTParserModule.java
new file mode 100644
index 0000000000..5ba2b1a5d7
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTParserModule.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.config;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import javax.inject.Singleton;
+
+import org.jclouds.dynect.v3.domain.SessionCredentials;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
+
+/**
+ * @author Adrian Cole
+ */
+public class DynECTParserModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ }
+
+ @Provides
+ @Singleton
+ public Map provideCustomAdapterBindings() {
+ return new ImmutableMap.Builder()
+ .put(SessionCredentials.class, new SessionCredentialsTypeAdapter())
+ .build();
+ }
+
+ private static class SessionCredentialsTypeAdapter implements JsonSerializer {
+ @Override
+ public JsonElement serialize(SessionCredentials src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject metadataObject = new JsonObject();
+ metadataObject.addProperty("customer_name", src.getCustomerName());
+ metadataObject.addProperty("user_name", src.getUserName());
+ metadataObject.addProperty("password", src.getPassword());
+ return metadataObject;
+ }
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java
new file mode 100644
index 0000000000..ca25e60bc4
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/config/DynECTRestClientModule.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.config;
+
+import java.util.Map;
+
+import org.jclouds.dynect.v3.DynECTApi;
+import org.jclouds.dynect.v3.DynECTAsyncApi;
+import org.jclouds.dynect.v3.features.SessionApi;
+import org.jclouds.dynect.v3.features.SessionAsyncApi;
+import org.jclouds.rest.ConfiguresRestClient;
+import org.jclouds.rest.config.RestClientModule;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Configures the DynECT connection.
+ *
+ * @author Adrian Cole
+ */
+@ConfiguresRestClient
+public class DynECTRestClientModule extends RestClientModule {
+
+ public static final Map, Class>> DELEGATE_MAP = ImmutableMap., Class>> builder()
+ .put(SessionApi.class, SessionAsyncApi.class)
+ .build();
+
+ public DynECTRestClientModule() {
+ super(DELEGATE_MAP);
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Session.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Session.java
new file mode 100644
index 0000000000..1928f06ef4
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/Session.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds licenses this file
+ * to you under the Apache License, String 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.dynect.v3.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import com.google.common.base.Objects;
+
+/**
+ * @author Adrian Cole
+ */
+public final class Session {
+
+ public static Session forTokenAndVersion(String token, String version) {
+ return new Session(token, version);
+ }
+
+ private final String token;
+ private final String version;
+
+ @ConstructorProperties({"token", "version"})
+ private Session(String token, String version) {
+ this.token = checkNotNull(token, "token");
+ this.version = checkNotNull(version, "version for %s", token);
+ }
+
+ /**
+ * The authentication token
+ */
+ public String getToken() {
+ return token;
+ }
+
+ /**
+ * The current version of the server
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(token, version);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Session other = Session.class.cast(obj);
+ return Objects.equal(this.token, other.token) && Objects.equal(this.version, other.version);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).add("token", token).add("version", version).toString();
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/SessionCredentials.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/SessionCredentials.java
new file mode 100644
index 0000000000..87489a3c6a
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/domain/SessionCredentials.java
@@ -0,0 +1,135 @@
+package org.jclouds.dynect.v3.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import javax.inject.Named;
+
+import org.jclouds.domain.Credentials;
+
+import com.google.common.base.Objects;
+
+/**
+ * Session credentials for API authentication.
+ *
+ * @see
+ *
+ * @author Adrian Cole
+ */
+public final class SessionCredentials extends Credentials {
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public Builder toBuilder() {
+ return builder().from(this);
+ }
+
+ public final static class Builder extends Credentials.Builder {
+ private String customerName;
+ private String userName;
+ private String password;
+
+ /**
+ * @see SessionCredentials#getCustomerName()
+ */
+ public Builder customerName(String customerName) {
+ this.customerName = customerName;
+ return this;
+ }
+
+ @Override
+ public Builder identity(String identity) {
+ return userName(identity);
+ }
+
+ @Override
+ public Builder credential(String credential) {
+ return password(credential);
+ }
+
+ /**
+ * @see SessionCredentials#getUserName()
+ */
+ public Builder userName(String userName) {
+ this.userName = userName;
+ return this;
+ }
+
+ /**
+ * @see SessionCredentials#getPassword()
+ */
+ public Builder password(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public SessionCredentials build() {
+ return new SessionCredentials(customerName, userName, password);
+ }
+
+ public Builder from(SessionCredentials in) {
+ return this.userName(in.identity).password(in.credential).customerName(in.customerName);
+ }
+ }
+
+ @Named("customer_name")
+ private final String customerName;
+ @Named("user_name")
+ private final String userName;
+ private final String password;
+
+ @ConstructorProperties({ "customer_name", "user_name", "password" })
+ private SessionCredentials(String customerName, String userName, String password) {
+ super(checkNotNull(userName, "userName"), checkNotNull(password, "password for %s", userName));
+ this.userName = userName;
+ this.password = password;
+ this.customerName = checkNotNull(customerName, "customerName for %s", userName);
+ }
+
+ /**
+ * UserName ID that identifies the temporary credentials.
+ */
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * The Secret Access Key to sign requests.
+ */
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * The security token that users must pass to the service API to use the
+ * temporary credentials.
+ */
+ public String getCustomerName() {
+ return customerName;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(customerName, userName, password);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ SessionCredentials other = SessionCredentials.class.cast(obj);
+ return Objects.equal(this.userName, other.userName) && Objects.equal(this.password, other.password)
+ && Objects.equal(this.customerName, other.customerName);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).add("userName", identity).add("customerName", customerName).toString();
+ }
+}
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionApi.java
new file mode 100644
index 0000000000..fc6fcf2816
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionApi.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.features;
+
+import org.jclouds.dynect.v3.domain.Session;
+import org.jclouds.dynect.v3.domain.SessionCredentials;
+
+/**
+ * @see SessionAsyncApi
+ * @see
+ * @author Adrian Cole
+ */
+public interface SessionApi {
+
+ Session login(SessionCredentials credentials);
+
+ boolean isValid(String token);
+
+ void logout(String token);
+}
\ No newline at end of file
diff --git a/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionAsyncApi.java b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionAsyncApi.java
new file mode 100644
index 0000000000..20d3ed6d21
--- /dev/null
+++ b/labs/dynect/src/main/java/org/jclouds/dynect/v3/features/SessionAsyncApi.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import javax.inject.Named;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.jclouds.dynect.v3.DynECTFallbacks.FalseOn400;
+import org.jclouds.dynect.v3.domain.Session;
+import org.jclouds.dynect.v3.domain.SessionCredentials;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.Headers;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Provides access to DynECT Managed DNS through the API2 api
+ *
+ *
+ * @see SessionApi
+ * @see
+ * @author Adrian Cole
+ */
+// required for all calls
+@Produces(APPLICATION_JSON)
+@Headers(keys = "API-Version", values = "{jclouds.api-version}")
+@Path("/Session")
+public interface SessionAsyncApi {
+
+ /**
+ * @see SessionApi#create
+ */
+ @Named("POST:Session")
+ @POST
+ @SelectJson("data")
+ ListenableFuture login(@BinderParam(BindToJsonPayload.class) SessionCredentials credentials);
+
+ /**
+ * @see SessionApi#isValid
+ */
+ @Named("GET:Session")
+ @GET
+ @Fallback(FalseOn400.class)
+ ListenableFuture isValid(@HeaderParam("Auth-Token") String token);
+
+ /**
+ * @see SessionApi#logout
+ */
+ @Named("DELETE:Session")
+ @DELETE
+ ListenableFuture logout(@HeaderParam("Auth-Token") String token);
+}
diff --git a/labs/dynect/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata b/labs/dynect/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
new file mode 100644
index 0000000000..10bc2961f3
--- /dev/null
+++ b/labs/dynect/src/main/resources/META-INF/services/org.jclouds.providers.ProviderMetadata
@@ -0,0 +1 @@
+org.jclouds.dynect.v3.DynECTProviderMetadata
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/DynECTProviderMetadataTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/DynECTProviderMetadataTest.java
new file mode 100644
index 0000000000..3518f321fb
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/DynECTProviderMetadataTest.java
@@ -0,0 +1,35 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3;
+
+import org.jclouds.dynect.v3.DynECTApiMetadata;
+import org.jclouds.dynect.v3.DynECTProviderMetadata;
+import org.jclouds.providers.internal.BaseProviderMetadataTest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "DynECTProviderMetadataTest")
+public class DynECTProviderMetadataTest extends BaseProviderMetadataTest {
+ public DynECTProviderMetadataTest() {
+ super(new DynECTProviderMetadata(), new DynECTApiMetadata());
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiExpectTest.java
new file mode 100644
index 0000000000..3cbacf2fa3
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiExpectTest.java
@@ -0,0 +1,97 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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
+ *
+ * Unles 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 expres or implied. See the License for the
+ * specific language governing permisions and limitations
+ * under the License.
+ */
+package org.jclouds.dynect.v3.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.jclouds.dynect.v3.DynECTApi;
+import org.jclouds.dynect.v3.domain.SessionCredentials;
+import org.jclouds.dynect.v3.internal.BaseDynECTApiExpectTest;
+import org.jclouds.dynect.v3.parse.ParseSessionTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpResponse;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit", testName = "SessionApiExpectTest")
+public class SessionApiExpectTest extends BaseDynECTApiExpectTest {
+
+ private String authToken = "FFFFFFFFFF";
+
+ HttpRequest create = HttpRequest.builder().method("POST")
+ .endpoint("https://api2.dynect.net/REST/Session")
+ .addHeader("API-Version", "3.3.7")
+ .payload(payloadFromStringWithContentType("{\"customer_name\":\"jclouds\",\"user_name\":\"joe\",\"password\":\"letmein\"}",APPLICATION_JSON))
+ .build();
+
+ HttpResponse createResponse = HttpResponse.builder().statusCode(200)
+ .payload(payloadFromResourceWithContentType("/create_session.json", APPLICATION_JSON)).build();
+
+ public void testCreateWhenResponseIs2xx() {
+ DynECTApi apiCreatesSession = requestSendsResponse(create, createResponse);
+ assertEquals(apiCreatesSession.getSessionApi().login(SessionCredentials.builder()
+ .customerName("jclouds")
+ .userName("joe")
+ .password("letmein").build()).toString(),
+ new ParseSessionTest().expected().toString());
+ }
+
+ HttpRequest isValid = HttpRequest.builder().method("GET")
+ .endpoint("https://api2.dynect.net/REST/Session")
+ .addHeader("API-Version", "3.3.7")
+ .addHeader("Auth-Token", authToken)
+ .payload(emptyJsonPayload())
+ .build();
+
+ HttpResponse validResponse = HttpResponse.builder().statusCode(200)
+ .payload(payloadFromResourceWithContentType("/session_valid.json", APPLICATION_JSON)).build();
+
+ HttpResponse invalidResponse = HttpResponse.builder().statusCode(400)
+ .payload(payloadFromResourceWithContentType("/session_invalid.json", APPLICATION_JSON)).build();
+
+ public void testSessionValid() {
+ DynECTApi apiWhenValid = requestSendsResponse(isValid, validResponse);
+ assertTrue(apiWhenValid.getSessionApi().isValid(authToken));
+ }
+
+ public void testSessionInvalid() {
+ DynECTApi apiWhenInvalid = requestSendsResponse(isValid, invalidResponse);
+ assertFalse(apiWhenInvalid.getSessionApi().isValid(authToken));
+ }
+
+ HttpRequest logout = HttpRequest.builder().method("DELETE")
+ .endpoint("https://api2.dynect.net/REST/Session")
+ .addHeader("API-Version", "3.3.7")
+ .addHeader("Auth-Token", authToken)
+ .payload(emptyJsonPayload())
+ .build();
+
+ HttpResponse logoutResponse = HttpResponse.builder().statusCode(200)
+ .payload(payloadFromResourceWithContentType("/logout.json", APPLICATION_JSON)).build();
+
+ public void testLogout() {
+ DynECTApi apiWhenLogoutSuccess = requestSendsResponse(logout, logoutResponse);
+ apiWhenLogoutSuccess.getSessionApi().logout(authToken);
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiLiveTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiLiveTest.java
new file mode 100644
index 0000000000..ab44ac23a7
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/features/SessionApiLiveTest.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.features;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.dynect.v3.domain.Session;
+import org.jclouds.dynect.v3.domain.SessionCredentials;
+import org.jclouds.dynect.v3.internal.BaseDynECTApiLiveTest;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "live", testName = "SessionApiLiveTest")
+public class SessionApiLiveTest extends BaseDynECTApiLiveTest {
+
+ private Session session;
+
+ private void checkSession(Session zone) {
+ checkNotNull(zone.getToken(), "Token cannot be null for a Session.");
+ checkNotNull(zone.getVersion(), "Version cannot be null for a Session.");
+ }
+
+ @Test
+ protected void testCreateSession() {
+ SessionCredentials credentials = SessionCredentials.builder()
+ .customerName(identity.substring(0, identity.indexOf(':')))
+ .userName(identity.substring(identity.indexOf(':') + 1))
+ .password(credential).build();
+ session = api().login(credentials);
+ checkSession(session);
+ }
+
+ protected SessionApi api() {
+ return context.getApi().getSessionApi();
+ }
+
+ @AfterClass(groups = { "integration", "live" })
+ @Override
+ protected void tearDownContext() {
+ if (session != null)
+ api().logout(session.getToken());
+ super.tearDownContext();
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiExpectTest.java
new file mode 100644
index 0000000000..deb3cd0acd
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiExpectTest.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.internal;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import org.jclouds.dynect.v3.DynECTApi;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+
+/**
+ * Base class for writing DynECT Expect tests
+ *
+ * @author Adrian Cole
+ */
+public class BaseDynECTApiExpectTest extends BaseDynECTExpectTest {
+
+ public static Payload emptyJsonPayload() {
+ Payload p = Payloads.newByteArrayPayload(new byte[] {});
+ p.getContentMetadata().setContentType(APPLICATION_JSON);
+ return p;
+ }
+
+ @Override
+ protected HttpRequestComparisonType compareHttpRequestAsType(HttpRequest input) {
+ return HttpRequestComparisonType.JSON;
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiLiveTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiLiveTest.java
new file mode 100644
index 0000000000..c8bed8f663
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTApiLiveTest.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.internal;
+
+import org.jclouds.apis.BaseContextLiveTest;
+import org.jclouds.dynect.v3.DynECTApi;
+import org.jclouds.dynect.v3.DynECTApiMetadata;
+import org.jclouds.dynect.v3.DynECTAsyncApi;
+import org.jclouds.rest.RestContext;
+import org.testng.annotations.Test;
+
+import com.google.common.reflect.TypeToken;
+
+@Test(groups = "live")
+public class BaseDynECTApiLiveTest extends BaseContextLiveTest> {
+
+ public BaseDynECTApiLiveTest() {
+ provider = "dynect";
+ }
+
+ @Override
+ protected TypeToken> contextType() {
+ return DynECTApiMetadata.CONTEXT_TOKEN;
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java
new file mode 100644
index 0000000000..8fb7fedcdc
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTExpectTest.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.internal;
+
+import org.jclouds.rest.internal.BaseRestApiExpectTest;
+
+/**
+ * Base class for writing DynECT Expect tests
+ *
+ * @author Adrian Cole
+ */
+public class BaseDynECTExpectTest extends BaseRestApiExpectTest {
+ public BaseDynECTExpectTest() {
+ provider = "dynect";
+ }
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTParseTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTParseTest.java
new file mode 100644
index 0000000000..333a15e5df
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/internal/BaseDynECTParseTest.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.internal;
+
+import org.jclouds.dynect.v3.config.DynECTParserModule;
+import org.jclouds.json.BaseItemParserTest;
+import org.jclouds.json.config.GsonModule;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * @author David Alves
+ */
+public abstract class BaseDynECTParseTest extends BaseItemParserTest {
+
+ @Override
+ protected Injector injector() {
+ return Guice.createInjector(new GsonModule(), new DynECTParserModule());
+ }
+
+}
diff --git a/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/ParseSessionTest.java b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/ParseSessionTest.java
new file mode 100644
index 0000000000..e9b353b6ef
--- /dev/null
+++ b/labs/dynect/src/test/java/org/jclouds/dynect/v3/parse/ParseSessionTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.dynect.v3.parse;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.core.MediaType;
+
+import org.jclouds.dynect.v3.domain.Session;
+import org.jclouds.dynect.v3.internal.BaseDynECTParseTest;
+import org.jclouds.rest.annotations.SelectJson;
+import org.testng.annotations.Test;
+
+/**
+ * @author Adrian Cole
+ */
+@Test(groups = "unit")
+public class ParseSessionTest extends BaseDynECTParseTest {
+
+ @Override
+ public String resource() {
+ return "/create_session.json";
+ }
+
+ @Override
+ @SelectJson("data")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Session expected() {
+ return Session.forTokenAndVersion("FFFFFFFFFF", "3.3.7");
+ }
+}
diff --git a/labs/dynect/src/test/resources/create_session.json b/labs/dynect/src/test/resources/create_session.json
new file mode 100644
index 0000000000..7b4860fda5
--- /dev/null
+++ b/labs/dynect/src/test/resources/create_session.json
@@ -0,0 +1 @@
+{"status": "success", "data": {"token": "FFFFFFFFFF", "version": "3.3.7"}, "job_id": 254417252, "msgs": [{"INFO": "login: Login successful", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]}
\ No newline at end of file
diff --git a/labs/dynect/src/test/resources/logback.xml b/labs/dynect/src/test/resources/logback.xml
new file mode 100644
index 0000000000..9679b2e03a
--- /dev/null
+++ b/labs/dynect/src/test/resources/logback.xml
@@ -0,0 +1,38 @@
+
+
+
+ target/test-data/jclouds.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+ target/test-data/jclouds-wire.log
+
+
+ %d %-5p [%c] [%thread] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/labs/dynect/src/test/resources/logout.json b/labs/dynect/src/test/resources/logout.json
new file mode 100644
index 0000000000..c14c1e892a
--- /dev/null
+++ b/labs/dynect/src/test/resources/logout.json
@@ -0,0 +1 @@
+{"status": "success", "data": {}, "job_id": 254506373, "msgs": [{"INFO": "logout: Logout successful", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]}
\ No newline at end of file
diff --git a/labs/dynect/src/test/resources/session_invalid.json b/labs/dynect/src/test/resources/session_invalid.json
new file mode 100644
index 0000000000..d3969415ce
--- /dev/null
+++ b/labs/dynect/src/test/resources/session_invalid.json
@@ -0,0 +1 @@
+{"status": "failure", "data": {}, "job_id": 254505048, "msgs": [{"INFO": "login: Bad or expired credentials", "SOURCE": "BLL", "ERR_CD": "INVALID_DATA", "LVL": "ERROR"}, {"INFO": "login: There was a problem with your credentials", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]}
\ No newline at end of file
diff --git a/labs/dynect/src/test/resources/session_valid.json b/labs/dynect/src/test/resources/session_valid.json
new file mode 100644
index 0000000000..6ca8158b06
--- /dev/null
+++ b/labs/dynect/src/test/resources/session_valid.json
@@ -0,0 +1 @@
+{"status": "success", "data": {}, "job_id": 254504748, "msgs": [{"INFO": "isalive: User session is still active", "SOURCE": "BLL", "ERR_CD": null, "LVL": "INFO"}]}
\ No newline at end of file
diff --git a/labs/pom.xml b/labs/pom.xml
index 09e8be00d2..2ecac0a6d8 100644
--- a/labs/pom.xml
+++ b/labs/pom.xml
@@ -64,6 +64,7 @@
openstack-quantum
openstack-glance
route53
+ dynect
aws-route53