Merge remote-tracking branch 'jclouds-labs-local/packet-moved' into promoted-packet

This commit is contained in:
Ignasi Barrera 2017-05-18 23:54:09 +02:00
commit c757906fb5
91 changed files with 9083 additions and 0 deletions

View File

@ -0,0 +1,10 @@
# Apache jclouds Packet (packet.net) provider
## Pre-requisites
If you are using Oracle JDK, you may get this exception
```
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
```
when calling Packet endpoints. To solve this, you want to install JCE following the [official documentation](http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html).

145
providers/packet/pom.xml Normal file
View File

@ -0,0 +1,145 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<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>
<parent>
<groupId>org.apache.jclouds.labs</groupId>
<artifactId>jclouds-labs</artifactId>
<version>2.1.0-SNAPSHOT</version>
</parent>
<!-- TODO: when out of labs, switch to org.jclouds.provider -->
<artifactId>packet</artifactId>
<name>jclouds Packet compute API</name>
<description>jclouds components to access an implementation of Packet's Compute Service</description>
<packaging>bundle</packaging>
<properties>
<test.packet.endpoint>https://api.packet.net/</test.packet.endpoint>
<test.packet.identity>projectId</test.packet.identity>
<test.packet.credential>api key</test.packet.credential>
<jclouds.osgi.export>org.jclouds.packet*;version="${project.version}"</jclouds.osgi.export>
<jclouds.osgi.import>org.jclouds*;version="${project.version}",*</jclouds.osgi.import>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-core</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds</groupId>
<artifactId>jclouds-compute</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.driver</groupId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.parent.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.driver</groupId>
<artifactId>jclouds-slf4j</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.jclouds.driver</groupId>
<artifactId>jclouds-sshj</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>mockwebserver</artifactId>
<scope>test</scope>
<exclusions>
<!-- Already provided by jclouds-sshj -->
<exclusion>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<defaultGoal>clean verify</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<systemPropertyVariables>
<test.packet.endpoint>${test.packet.endpoint}</test.packet.endpoint>
<test.packet.identity>${test.packet.identity}</test.packet.identity>
<test.packet.credential>${test.packet.credential}</test.packet.credential>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,86 @@
/*
* 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.packet;
import java.io.Closeable;
import javax.ws.rs.PathParam;
import org.jclouds.packet.features.DeviceApi;
import org.jclouds.packet.features.FacilityApi;
import org.jclouds.packet.features.OperatingSystemApi;
import org.jclouds.packet.features.PlanApi;
import org.jclouds.packet.features.ProjectApi;
import org.jclouds.packet.features.SshKeyApi;
import org.jclouds.rest.annotations.Delegate;
/**
* The Packet API is a REST API for managing your services and deployments.
* <p>
*
* @see <a href="https://www.packet.net/help/api/" >doc</a>
*/
public interface PacketApi extends Closeable {
/**
* The Packet API includes operations for managing project.
*
* @see <a href="https://www.packet.net/help/api/#page:projects,header:projects-projects">docs</a>
*/
@Delegate
ProjectApi projectApi();
/**
* This Packet API provides all of the devices
*
* @see <a href="https://www.packet.net/help/api/#page:devices">docs</a>
*/
@Delegate
DeviceApi deviceApi(@PathParam("projectId") String projectId);
/**
* This Packet API provides all of the facilities
*
* @see <a href="https://www.packet.net/help/api/#page:devices,header:devices-operating-systems">docs</a>
*/
@Delegate
FacilityApi facilityApi();
/**
* This Packet API provides all of the plans
*
* @see <a href="https://www.packet.net/help/api/#page:devices,header:devices-plans">docs</a>
*/
@Delegate
PlanApi planApi();
/**
* This Packet API provides all of the operating systems
*
* @see <a href="https://www.packet.net/help/api/#page:devices,header:devices-operating-systems">docs</a>
*/
@Delegate
OperatingSystemApi operatingSystemApi();
/**
* This Packet API provides all of the operating systems
*
* @see <a href="https://www.packet.net/help/api/#page:ssh-keys">docs</a>
*/
@Delegate
SshKeyApi sshKeyApi();
}

View File

@ -0,0 +1,92 @@
/*
* 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.packet;
import java.net.URI;
import java.util.Properties;
import org.jclouds.apis.ApiMetadata;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.packet.compute.config.PacketComputeServiceContextModule;
import org.jclouds.packet.config.PacketComputeParserModule;
import org.jclouds.packet.config.PacketHttpApiModule;
import org.jclouds.rest.internal.BaseHttpApiMetadata;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import static org.jclouds.compute.config.ComputeServiceProperties.TEMPLATE;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.reflect.Reflection2.typeToken;
/**
* Implementation of {@link ApiMetadata} for Packet API
*/
public class PacketApiMetadata extends BaseHttpApiMetadata<PacketApi> {
@Override
public Builder toBuilder() {
return new Builder().fromApiMetadata(this);
}
public PacketApiMetadata() {
this(new Builder());
}
protected PacketApiMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
Properties properties = BaseHttpApiMetadata.defaultProperties();
properties.put(TEMPLATE, "osFamily=UBUNTU,os64Bit=true,osVersionMatches=16.*");
properties.put(TIMEOUT_NODE_RUNNING, 900000); // 15 mins
properties.put(TIMEOUT_NODE_SUSPENDED, 900000); // 15 mins
return properties;
}
public static class Builder extends BaseHttpApiMetadata.Builder<PacketApi, Builder> {
protected Builder() {
id("packet")
.name("Packet API")
.identityName("Packet Project Id")
.credentialName("Must be Packet Token")
.documentation(URI.create("https://www.packet.net/help/api/#"))
.defaultEndpoint("https://api.packet.net")
.defaultProperties(PacketApiMetadata.defaultProperties())
.version("1")
.view(typeToken(ComputeServiceContext.class))
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
.add(PacketHttpApiModule.class)
.add(PacketComputeParserModule.class)
.add(PacketComputeServiceContextModule.class)
.build());
}
@Override
public PacketApiMetadata build() {
return new PacketApiMetadata(this);
}
@Override
protected Builder self() {
return this;
}
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.packet;
import java.net.URI;
import java.util.Properties;
import org.jclouds.providers.ProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadata;
import com.google.auto.service.AutoService;
@AutoService(ProviderMetadata.class)
public class PacketProviderMetadata extends BaseProviderMetadata {
public static Builder builder() {
return new Builder();
}
@Override
public Builder toBuilder() {
return builder().fromProviderMetadata(this);
}
public PacketProviderMetadata() {
super(builder());
}
public PacketProviderMetadata(Builder builder) {
super(builder);
}
public static Properties defaultProperties() {
final Properties properties = PacketApiMetadata.defaultProperties();
return properties;
}
public static class Builder extends BaseProviderMetadata.Builder {
protected Builder() {
id("packet")
.name("Packet Compute Services")
.apiMetadata(new PacketApiMetadata())
.homepage(URI.create("https://www.packet.net/"))
.console(URI.create("https://app.packet.net/portal"))
.endpoint("https://api.packet.net")
.iso3166Codes("US-CA", "US-NJ", "NL", "JP")
.defaultProperties(PacketProviderMetadata.defaultProperties());
}
@Override
public PacketProviderMetadata build() {
return new PacketProviderMetadata(this);
}
@Override
public Builder fromProviderMetadata(ProviderMetadata in) {
super.fromProviderMetadata(in);
return this;
}
}
}

View File

@ -0,0 +1,184 @@
/*
* 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.packet.compute;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.domain.Credentials;
import org.jclouds.location.Provider;
import org.jclouds.logging.Logger;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.compute.options.PacketTemplateOptions;
import org.jclouds.packet.domain.BillingCycle;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.Facility;
import org.jclouds.packet.domain.OperatingSystem;
import org.jclouds.packet.domain.Plan;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
/**
* defines the connection between the {@link org.jclouds.packet.PacketApi} implementation and
* the jclouds {@link org.jclouds.compute.ComputeService}
*/
@Singleton
public class PacketComputeServiceAdapter implements ComputeServiceAdapter<Device, Plan, OperatingSystem, Facility> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final PacketApi api;
private final String projectId;
@Inject
PacketComputeServiceAdapter(PacketApi api, @Provider final Supplier<Credentials> creds) {
this.api = api;
this.projectId = creds.get().identity;
}
@Override
public NodeAndInitialCredentials<Device> createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
PacketTemplateOptions templateOptions = template.getOptions().as(PacketTemplateOptions.class);
Map<String, String> features = templateOptions.getFeatures();
BillingCycle billingCycle = BillingCycle.fromValue(templateOptions.getBillingCycle());
boolean locked = templateOptions.isLocked();
String userdata = templateOptions.getUserData();
Set<String> tags = templateOptions.getTags();
String plan = template.getHardware().getId();
String facility = template.getLocation().getId();
String operatingSystem = template.getImage().getId();
Device device = api.deviceApi(projectId).create(
Device.CreateDevice.builder()
.hostname(name)
.plan(plan)
.billingCycle(billingCycle.value())
.facility(facility)
.features(features)
.operatingSystem(operatingSystem)
.locked(locked)
.userdata(userdata)
.tags(tags)
.build()
);
// Any new servers you deploy to projects you are a collaborator on will have your project and personal SSH keys, if defined.
// If no SSH keys are defined in your account, jclouds will generate one usiing CreateSshKeysThenCreateNodes
// so that it will add it to the device with the default mechanism.
// Safe to pass null credentials here, as jclouds will default populate
// the node with the default credentials from the image, or the ones in
// the options, if provided.
return new NodeAndInitialCredentials<Device>(device, device.id(), null);
}
@Override
public Iterable<Plan> listHardwareProfiles() {
return Iterables.filter(api.planApi().list().concat(), new Predicate<Plan>() {
@Override
public boolean apply(Plan input) {
return input.line().equals("baremetal");
}
});
}
@Override
public Iterable<OperatingSystem> listImages() {
return api.operatingSystemApi().list().concat();
}
@Override
public OperatingSystem getImage(final String id) {
Optional<OperatingSystem> firstInterestingOperatingSystem = api
.operatingSystemApi().list()
.concat()
.firstMatch(new Predicate<OperatingSystem>() {
@Override
public boolean apply(OperatingSystem input) {
return input.slug().equals(id);
}
});
if (!firstInterestingOperatingSystem.isPresent()) {
throw new IllegalStateException("Cannot find image with the required slug " + id);
}
return firstInterestingOperatingSystem.get();
}
@Override
public Iterable<Facility> listLocations() {
return api.facilityApi().list().concat();
}
@Override
public Device getNode(String id) {
return api.deviceApi(projectId).get(id);
}
@Override
public void destroyNode(String id) {
api.deviceApi(projectId).delete(id);
}
@Override
public void rebootNode(String id) {
api.deviceApi(projectId).reboot(id);
}
@Override
public void resumeNode(String id) {
api.deviceApi(projectId).powerOn(id);
}
@Override
public void suspendNode(String id) {
api.deviceApi(projectId).powerOff(id);
}
@Override
public Iterable<Device> listNodes() {
return api.deviceApi(projectId).list().concat();
}
@Override
public Iterable<Device> listNodesByIds(final Iterable<String> ids) {
return filter(listNodes(), new Predicate<Device>() {
@Override
public boolean apply(Device device) {
return contains(ids, String.valueOf(device.id()));
}
});
}
}

View File

@ -0,0 +1,151 @@
/*
* 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.packet.compute.config;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
import org.jclouds.location.Provider;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.compute.PacketComputeServiceAdapter;
import org.jclouds.packet.compute.functions.DeviceStateToStatus;
import org.jclouds.packet.compute.functions.DeviceToNodeMetadata;
import org.jclouds.packet.compute.functions.FacilityToLocation;
import org.jclouds.packet.compute.functions.OperatingSystemToImage;
import org.jclouds.packet.compute.functions.PlanToHardware;
import org.jclouds.packet.compute.options.PacketTemplateOptions;
import org.jclouds.packet.compute.strategy.CreateSshKeysThenCreateNodes;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.Facility;
import org.jclouds.packet.domain.OperatingSystem;
import org.jclouds.packet.domain.Plan;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
import static org.jclouds.util.Predicates2.retry;
public class PacketComputeServiceContextModule extends
ComputeServiceAdapterContextModule<Device, Plan, OperatingSystem, Facility> {
@SuppressWarnings("unchecked")
@Override
protected void configure() {
super.configure();
bind(new TypeLiteral<ComputeServiceAdapter<Device, Plan, OperatingSystem, Facility>>() {
}).to(PacketComputeServiceAdapter.class);
bind(new TypeLiteral<Function<Device, NodeMetadata>>() {
}).to(DeviceToNodeMetadata.class);
bind(new TypeLiteral<Function<Plan, Hardware>>() {
}).to(PlanToHardware.class);
bind(new TypeLiteral<Function<OperatingSystem, Image>>() {
}).to(OperatingSystemToImage.class);
bind(new TypeLiteral<Function<Facility, Location>>() {
}).to(FacilityToLocation.class);
bind(new TypeLiteral<Function<Device.State, NodeMetadata.Status>>() {
}).to(DeviceStateToStatus.class);
install(new LocationsFromComputeServiceAdapterModule<Device, Plan, OperatingSystem, Facility>() {
});
bind(TemplateOptions.class).to(PacketTemplateOptions.class);
bind(CreateNodesInGroupThenAddToSet.class).to(CreateSshKeysThenCreateNodes.class);
}
@Provides
@Named(TIMEOUT_NODE_RUNNING)
protected Predicate<String> provideDeviceRunningPredicate(final PacketApi api,
@Provider final Supplier<Credentials> creds,
ComputeServiceConstants.Timeouts timeouts,
ComputeServiceConstants.PollPeriod pollPeriod) {
return retry(new DeviceInStatusPredicate(api, creds.get().identity, Device.State.ACTIVE), timeouts.nodeRunning,
pollPeriod.pollInitialPeriod, pollPeriod.pollMaxPeriod);
}
@Provides
@Named(TIMEOUT_NODE_SUSPENDED)
protected Predicate<String> provideDeviceSuspendedPredicate(final PacketApi api, @Provider final Supplier<Credentials> creds, ComputeServiceConstants.Timeouts timeouts,
ComputeServiceConstants.PollPeriod pollPeriod) {
return retry(new DeviceInStatusPredicate(api, creds.get().identity, Device.State.INACTIVE), timeouts.nodeSuspended,
pollPeriod.pollInitialPeriod, pollPeriod.pollMaxPeriod);
}
@Provides
@Named(TIMEOUT_NODE_TERMINATED)
protected Predicate<String> provideDeviceTerminatedPredicate(final PacketApi api, @Provider final Supplier<Credentials> creds, ComputeServiceConstants.Timeouts timeouts,
ComputeServiceConstants.PollPeriod pollPeriod) {
return retry(new DeviceTerminatedPredicate(api, creds.get().identity), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
pollPeriod.pollMaxPeriod);
}
@VisibleForTesting
static class DeviceInStatusPredicate implements Predicate<String> {
private final PacketApi api;
private final String projectId;
private final Device.State state;
public DeviceInStatusPredicate(PacketApi api, String projectId, Device.State state) {
this.api = checkNotNull(api, "api must not be null");
this.projectId = checkNotNull(projectId, "projectId must not be null");
this.state = checkNotNull(state, "state must not be null");
}
@Override
public boolean apply(String input) {
checkNotNull(input, "device id");
Device device = api.deviceApi(projectId).get(input);
return device != null && state == device.state();
}
}
@VisibleForTesting
static class DeviceTerminatedPredicate implements Predicate<String> {
private final PacketApi api;
private final String projectId;
public DeviceTerminatedPredicate(PacketApi api, String projectId) {
this.api = checkNotNull(api, "api must not be null");
this.projectId = checkNotNull(projectId, "projectId must not be null");
}
@Override
public boolean apply(String input) {
checkNotNull(input, "device id");
Device device = api.deviceApi(projectId).get(input);
return device == null;
}
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.packet.compute.functions;
import javax.inject.Singleton;
import org.jclouds.compute.domain.NodeMetadata.Status;
import org.jclouds.packet.domain.Device;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableMap;
/**
* Transforms an {@link Device.State} to the jclouds portable model.
*/
@Singleton
public class DeviceStateToStatus implements Function<Device.State, Status> {
private static final Function<Device.State, Status> toPortableStatus = Functions.forMap(
ImmutableMap.<Device.State, Status> builder()
.put(Device.State.PROVISIONING, Status.PENDING)
.put(Device.State.POWERING_ON, Status.PENDING)
.put(Device.State.POWERING_OFF, Status.PENDING)
.put(Device.State.REBOOTING, Status.PENDING)
.put(Device.State.QUEUED, Status.PENDING)
.put(Device.State.INACTIVE, Status.SUSPENDED)
.put(Device.State.ACTIVE, Status.RUNNING)
.build(),
Status.UNRECOGNIZED);
@Override
public Status apply(final Device.State input) {
return toPortableStatus.apply(input);
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.packet.compute.functions;
import java.util.List;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.IpAddress;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import static com.google.common.collect.FluentIterable.from;
/**
* Transforms an {@link Device} to the jclouds portable model.
*/
@Singleton
public class DeviceToNodeMetadata implements Function<Device, NodeMetadata> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final PlanToHardware planToHardware;
private final OperatingSystemToImage operatingSystemToImage;
private final FacilityToLocation facilityToLocation;
private final Function<Device.State, NodeMetadata.Status> toPortableStatus;
private final GroupNamingConvention groupNamingConvention;
@Inject
DeviceToNodeMetadata(PlanToHardware planToHardware, OperatingSystemToImage operatingSystemToImage, FacilityToLocation facilityToLocation,
Function<Device.State, NodeMetadata.Status> toPortableStatus,
GroupNamingConvention.Factory groupNamingConvention) {
this.planToHardware = planToHardware;
this.operatingSystemToImage = operatingSystemToImage;
this.facilityToLocation = facilityToLocation;
this.toPortableStatus = toPortableStatus;
this.groupNamingConvention = groupNamingConvention.createWithoutPrefix();
}
@Override
public NodeMetadata apply(Device input) {
return new NodeMetadataBuilder()
.ids(input.id())
.name(input.hostname())
.hostname(String.format("%s", input.hostname()))
.group(groupNamingConvention.extractGroup(input.hostname()))
.location(facilityToLocation.apply(input.facility()))
.hardware(planToHardware.apply(input.plan()))
.imageId(input.operatingSystem().slug())
.operatingSystem(operatingSystemToImage.apply(input.operatingSystem()).getOperatingSystem())
.status(toPortableStatus.apply(input.state()))
.publicAddresses(getPublicIpAddresses(input.ipAddresses()))
.privateAddresses(getPrivateIpAddresses(input.ipAddresses()))
.tags(input.tags())
.build();
}
private Iterable<String> getPublicIpAddresses(List<IpAddress> input) {
return filterAndTransformIpAddresses(input, new IsPublicIpAddress());
}
private Iterable<String> getPrivateIpAddresses(List<IpAddress> input) {
return filterAndTransformIpAddresses(input, Predicates.not(new IsPublicIpAddress()));
}
private Iterable<String> filterAndTransformIpAddresses(List<IpAddress> input, Predicate<IpAddress> filter) {
return from(input).filter(filter).transform(new IpAddressToIp());
}
private static class IpAddressToIp implements Function<IpAddress, String> {
@Override
public String apply(final IpAddress input) {
return input.address();
}
}
private static class IsPublicIpAddress implements Predicate<IpAddress> {
@Override
public boolean apply(IpAddress input) {
return input.publicAddress();
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.packet.compute.functions;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.domain.Location;
import org.jclouds.domain.LocationBuilder;
import org.jclouds.domain.LocationScope;
import org.jclouds.location.suppliers.all.JustProvider;
import org.jclouds.packet.domain.Facility;
import com.google.common.base.Function;
import static com.google.common.collect.Iterables.getOnlyElement;
/**
* Transforms an {@link Facility} to the jclouds portable model.
*/
@Singleton
public class FacilityToLocation implements Function<Facility, Location> {
private final JustProvider justProvider;
// allow us to lazy discover the provider of a resource
@Inject
FacilityToLocation(JustProvider justProvider) {
this.justProvider = justProvider;
}
@Override
public Location apply(final Facility facility) {
final LocationBuilder builder = new LocationBuilder();
builder.id(facility.code());
builder.description(facility.name());
builder.parent(getOnlyElement(justProvider.get()));
builder.scope(LocationScope.REGION);
return builder.build();
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.packet.compute.functions;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.domain.ImageBuilder;
import org.jclouds.packet.domain.Distribution;
import org.jclouds.packet.domain.OperatingSystem;
import com.google.common.base.Function;
import static org.jclouds.compute.domain.OperatingSystem.builder;
/**
* Transforms an {@link OperatingSystem} to the jclouds portable model.
*/
@Singleton
public class OperatingSystemToImage implements Function<OperatingSystem, Image> {
@Override
public Image apply(final OperatingSystem input) {
ImageBuilder builder = new ImageBuilder();
builder.ids(input.slug());
builder.name(input.name());
builder.description(input.name());
builder.status(Image.Status.AVAILABLE);
builder.operatingSystem(builder()
.name(input.name())
.family(Distribution.fromValue(input.distribution()).osFamily())
.description(input.name())
.version(input.version())
.is64Bit(true)
.build());
return builder.build();
}
}

View File

@ -0,0 +1,86 @@
/*
* 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.packet.compute.functions;
import javax.inject.Singleton;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.HardwareBuilder;
import org.jclouds.compute.domain.Processor;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.domain.internal.VolumeImpl;
import org.jclouds.packet.domain.Plan;
import org.jclouds.packet.domain.Specs;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* Transforms an {@link Plan} to the jclouds portable model.
*/
@Singleton
public class PlanToHardware implements Function<Plan, Hardware> {
@Override
public Hardware apply(Plan plan) {
HardwareBuilder builder = new HardwareBuilder()
.ids(plan.slug())
.name(plan.name())
.hypervisor("none")
.processors(getProcessors(plan))
.ram(getMemory(plan))
.volumes(getVolumes(plan));
return builder.build();
}
private Integer getMemory(Plan plan) {
if (plan.specs() == null || plan.specs().drives() == null) return 0;
String total = plan.specs().memory().total();
if (total.endsWith("GB")) {
return Integer.valueOf(total.substring(0, total.length() - 2)) * 1024;
} else {
throw new IllegalArgumentException("Cannot parse memory: " + plan.specs().memory());
}
}
private Iterable<Volume> getVolumes(Plan plan) {
if (plan.specs() == null || plan.specs().drives() == null) return Lists.newArrayList();
return Iterables.transform(plan.specs().drives(), new Function<Specs.Drive, Volume>() {
@Override
public Volume apply(Specs.Drive drive) {
return new VolumeImpl(
drive.type(),
Volume.Type.LOCAL,
Float.parseFloat(drive.size().substring(0, drive.size().length() - 2)), null, true, false);
}
});
}
private Iterable<Processor> getProcessors(Plan plan) {
if (plan.specs() == null || plan.specs().cpus() == null) return Lists.newArrayList();
return Iterables.transform(plan.specs().cpus(), new Function<Specs.CPU, Processor>() {
@Override
public Processor apply(Specs.CPU input) {
// No cpu speed from Packet API, so assume more cores == faster
return new Processor(input.count(), input.count());
}
});
}
}

View File

@ -0,0 +1,158 @@
/*
* 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.packet.compute.options;
import java.util.Map;
import org.jclouds.compute.options.TemplateOptions;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.ImmutableMap;
import static com.google.common.base.Objects.equal;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Custom options for the Packet API.
*/
public class PacketTemplateOptions extends TemplateOptions implements Cloneable {
private Map<String, String> features = ImmutableMap.of();
private boolean locked = false;
private String billingCycle = "hourly";
private String userData = "";
public PacketTemplateOptions features(Map<String, String> features) {
this.features = ImmutableMap.copyOf(checkNotNull(features, "features cannot be null"));
return this;
}
public PacketTemplateOptions locked(boolean locked) {
this.locked = locked;
return this;
}
public PacketTemplateOptions billingCycle(String billingCycle) {
this.billingCycle = billingCycle;
return this;
}
public PacketTemplateOptions userData(String userData) {
this.userData = userData;
return this;
}
public Map<String, String> getFeatures() {
return features;
}
public boolean isLocked() {
return locked;
}
public String getBillingCycle() {
return billingCycle;
}
public String getUserData() {
return userData;
}
@Override
public PacketTemplateOptions clone() {
PacketTemplateOptions options = new PacketTemplateOptions();
copyTo(options);
return options;
}
@Override
public void copyTo(TemplateOptions to) {
super.copyTo(to);
if (to instanceof PacketTemplateOptions) {
PacketTemplateOptions eTo = PacketTemplateOptions.class.cast(to);
eTo.features(features);
eTo.locked(locked);
eTo.billingCycle(billingCycle);
eTo.userData(userData);
}
}
@Override
public int hashCode() {
return Objects.hashCode(super.hashCode(), features, locked, billingCycle, userData);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
PacketTemplateOptions other = (PacketTemplateOptions) obj;
return super.equals(other) && equal(this.locked, other.locked) && equal(this.billingCycle, other.billingCycle) && equal(this.userData, other.userData) && equal(this.features, other.features);
}
@Override
public ToStringHelper string() {
ToStringHelper toString = super.string().omitNullValues();
if (!features.isEmpty()) {
toString.add("features", features);
} toString.add("locked", locked);
toString.add("billingCycle", billingCycle);
toString.add("userData", userData);
return toString;
}
public static class Builder {
/**
* @see PacketTemplateOptions#features
*/
public static PacketTemplateOptions features(Map<String, String> features) {
PacketTemplateOptions options = new PacketTemplateOptions();
return options.features(features);
}
/**
* @see PacketTemplateOptions#locked
*/
public static PacketTemplateOptions locked(boolean locked) {
PacketTemplateOptions options = new PacketTemplateOptions();
return options.locked(locked);
}
/**
* @see PacketTemplateOptions#billingCycle
*/
public static PacketTemplateOptions billingCycle(String billingCycle) {
PacketTemplateOptions options = new PacketTemplateOptions();
return options.billingCycle(billingCycle);
}
/**
* @see PacketTemplateOptions#userData
*/
public static PacketTemplateOptions userData(String userData) {
PacketTemplateOptions options = new PacketTemplateOptions();
return options.userData(userData);
}
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.packet.compute.strategy;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.compute.config.CustomizationResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
import org.jclouds.compute.strategy.ListNodesStrategy;
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
import org.jclouds.logging.Logger;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.compute.options.PacketTemplateOptions;
import org.jclouds.packet.domain.SshKey;
import org.jclouds.ssh.SshKeyPairGenerator;
import org.jclouds.ssh.SshKeys;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.size;
@Singleton
public class CreateSshKeysThenCreateNodes extends CreateNodesWithGroupEncodedIntoNameThenAddToSet {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final PacketApi api;
private final SshKeyPairGenerator keyGenerator;
@Inject
protected CreateSshKeysThenCreateNodes(
CreateNodeWithGroupEncodedIntoName addNodeWithGroupStrategy,
ListNodesStrategy listNodesStrategy,
GroupNamingConvention.Factory namingConvention,
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
PacketApi api, SshKeyPairGenerator keyGenerator) {
super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, userExecutor,
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
this.api = api;
this.keyGenerator = keyGenerator;
}
@Override
public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template,
Set<NodeMetadata> goodNodes, Map<NodeMetadata, Exception> badNodes,
Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
PacketTemplateOptions options = template.getOptions().as(PacketTemplateOptions.class);
Set<String> generatedSshKeyIds = Sets.newHashSet();
// If no key has been configured, generate a key pair
if (Strings.isNullOrEmpty(options.getPublicKey())) {
generateKeyPairAndAddKeyToSet(options, generatedSshKeyIds, group);
}
// If there is a script to run in the node, make sure a private key has
// been configured so jclouds will be able to access the node
if (options.getRunScript() != null && Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
logger.warn(">> A runScript has been configured but no SSH key has been provided."
+ " Authentication will delegate to the ssh-agent");
}
// If there is a key configured, then make sure there is a key pair for it
if (!Strings.isNullOrEmpty(options.getPublicKey())) {
createKeyPairForPublicKeyInOptionsAndAddToSet(options, generatedSshKeyIds);
}
Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
customizationResponses);
// Key pairs in Packet are only required to create the devices. They aren't used anymore so it is better
// to delete the auto-generated key pairs at this point where we know exactly which ones have been
// auto-generated by jclouds.
registerAutoGeneratedKeyPairCleanupCallbacks(responses, generatedSshKeyIds);
return responses;
}
private void createKeyPairForPublicKeyInOptionsAndAddToSet(PacketTemplateOptions options,
Set<String> generatedSshKeyIds) {
logger.debug(">> checking if the key pair already exists...");
PublicKey userKey;
Iterable<String> parts = Splitter.on(' ').split(options.getPublicKey());
checkArgument(size(parts) >= 2, "bad format, should be: ssh-rsa AAAAB3...");
String type = get(parts, 0);
try {
if ("ssh-rsa".equals(type)) {
RSAPublicKeySpec spec = SshKeys.publicKeySpecFromOpenSSH(options.getPublicKey());
userKey = KeyFactory.getInstance("RSA").generatePublic(spec);
} else {
throw new IllegalArgumentException("bad format, ssh-rsa is only supported");
}
} catch (InvalidKeySpecException ex) {
throw propagate(ex);
} catch (NoSuchAlgorithmException ex) {
throw propagate(ex);
}
String label = computeFingerprint(userKey);
SshKey key = api.sshKeyApi().get(label);
if (key == null) {
logger.debug(">> key pair not found. creating a new key pair %s ...", label);
SshKey newKey = api.sshKeyApi().create(label, options.getPublicKey());
logger.debug(">> key pair created! %s", newKey);
} else {
logger.debug(">> key pair found! %s", key);
generatedSshKeyIds.add(key.id());
}
}
private void generateKeyPairAndAddKeyToSet(PacketTemplateOptions options, Set<String> generatedSshKeyIds, String prefix) {
logger.debug(">> creating default keypair for node...");
Map<String, String> defaultKeys = keyGenerator.get();
SshKey sshKey = api.sshKeyApi().create(prefix + System.getProperty("user.name"), defaultKeys.get("public"));
generatedSshKeyIds.add(sshKey.id());
logger.debug(">> keypair created! %s", sshKey);
// If a private key has not been explicitly set, configure the generated one
if (Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
options.overrideLoginPrivateKey(defaultKeys.get("private"));
}
}
private void registerAutoGeneratedKeyPairCleanupCallbacks(Map<?, ListenableFuture<Void>> responses,
final Set<String> generatedSshKeyIds) {
// The Futures.allAsList fails immediately if some of the futures fail. The Futures.successfulAsList, however,
// returns a list containing the results or 'null' for those futures that failed. We want to wait for all them
// (even if they fail), so better use the latter form.
ListenableFuture<List<Void>> aggregatedResponses = Futures.successfulAsList(responses.values());
// Key pairs must be cleaned up after all futures completed (even if some failed).
Futures.addCallback(aggregatedResponses, new FutureCallback<List<Void>>() {
@Override
public void onSuccess(List<Void> result) {
cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
}
@Override
public void onFailure(Throwable t) {
cleanupAutoGeneratedKeyPairs(generatedSshKeyIds);
}
private void cleanupAutoGeneratedKeyPairs(Set<String> generatedSshKeyIds) {
logger.debug(">> cleaning up auto-generated key pairs...");
for (String sshKeyId : generatedSshKeyIds) {
try {
api.sshKeyApi().delete(sshKeyId);
} catch (Exception ex) {
logger.warn(">> could not delete key pair %s: %s", sshKeyId, ex.getMessage());
}
}
}
}, userExecutor);
}
private static String computeFingerprint(PublicKey key) {
if (key instanceof RSAPublicKey) {
RSAPublicKey rsaKey = (RSAPublicKey) key;
return SshKeys.fingerprint(rsaKey.getPublicExponent(), rsaKey.getModulus());
} else {
throw new IllegalArgumentException("Only RSA keys are supported");
}
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.packet.config;
import org.jclouds.json.config.GsonModule;
import com.google.inject.AbstractModule;
public class PacketComputeParserModule extends AbstractModule {
@Override
protected void configure() {
bind(GsonModule.DateAdapter.class).to(GsonModule.Iso8601DateAdapter.class);
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.packet.config;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.http.annotation.Redirection;
import org.jclouds.http.annotation.ServerError;
import org.jclouds.location.suppliers.ImplicitLocationSupplier;
import org.jclouds.location.suppliers.implicit.FirstRegion;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.functions.HrefToListOptions;
import org.jclouds.packet.handlers.PacketErrorHandler;
import org.jclouds.rest.ConfiguresHttpApi;
import org.jclouds.rest.config.HttpApiModule;
import com.google.common.base.Function;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
@ConfiguresHttpApi
public class PacketHttpApiModule extends HttpApiModule<PacketApi> {
@Override
protected void configure() {
super.configure();
bind(ImplicitLocationSupplier.class).to(FirstRegion.class).in(Scopes.SINGLETON);
bind(new TypeLiteral<Function<Href, ListOptions>>() {
}).to(HrefToListOptions.class);
}
@Override
protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(PacketErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(PacketErrorHandler.class);
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(PacketErrorHandler.class);
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.packet.domain;
/**
* Performs an action for the given device. Possible actions include:
power_on
power_off
reboot
rescue: reboot the device into rescue OS.
*/
public enum ActionType {
POWER_ON ("power_on"),
POWER_OFF ("power_off"),
REBOOT ("reboot"),
RESCUE ("rescue");
private final String type;
ActionType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.packet.domain;
import java.util.List;
import com.google.common.base.Predicate;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.tryFind;
import static java.util.Arrays.asList;
public enum BillingCycle {
HOURLY("hourly"),
MONTHLY("monthly"),
UNRECOGNIZED("");
private static final List<BillingCycle> values = asList(BillingCycle.values());
private final String value;
private BillingCycle(String value) {
this.value = checkNotNull(value, "value cannot be null");
}
public String value() {
return this.value;
}
public static BillingCycle fromValue(String value) {
return tryFind(values, hasValue(value)).or(UNRECOGNIZED);
}
private static Predicate<BillingCycle> hasValue(final String value) {
return new Predicate<BillingCycle>() {
@Override
public boolean apply(BillingCycle input) {
return input.value.equalsIgnoreCase(value);
}
};
}
}

View File

@ -0,0 +1,184 @@
/*
* 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.packet.domain;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.base.Enums;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import static com.google.common.base.Preconditions.checkArgument;
@AutoValue
public abstract class Device {
public enum State {
PROVISIONING, QUEUED, ACTIVE, REBOOTING, POWERING_OFF, POWERING_ON, INACTIVE;
public static State fromValue(String value) {
Optional<State> state = Enums.getIfPresent(State.class, value.toUpperCase());
checkArgument(state.isPresent(), "Expected one of %s but was %s", Joiner.on(',').join(State.values()), value);
return state.get();
}
}
public abstract String id();
public abstract String shortId();
public abstract String hostname();
@Nullable
public abstract String description();
public abstract State state();
public abstract List<String> tags();
public abstract String billingCycle();
public abstract String user();
public abstract String iqn();
public abstract Boolean locked();
public abstract String bondingMode();
public abstract Date createdAt();
public abstract Date updatedAt();
public abstract OperatingSystem operatingSystem();
public abstract Facility facility();
public abstract Href project();
public abstract List<Href> sshKeys();
public abstract Href projectLite();
public abstract List<Object> volumes();
public abstract List<IpAddress> ipAddresses();
public abstract List<ProvisioningEvent> provisioningEvents();
public abstract Plan plan();
@Nullable public abstract String rootPassword();
public abstract String userdata();
public abstract String href();
@SerializedNames({"id", "short_id", "hostname", "description", "state", "tags", "billing_cycle", "user", "iqn",
"locked", "bonding_mode", "created_at", "updated_at", "operating_system", "facility", "project", "ssh_keys",
"project_lite", "volumes", "ip_addresses", "provisioning_events", "plan", "root_password", "userdata", "href"})
public static Device create(String id,
String shortId,
String hostname,
String description,
State state,
List<String> tags,
String billingCycle,
String user,
String iqn,
Boolean locked,
String bondingMode,
Date createdAt,
Date updatedAt,
OperatingSystem operatingSystem,
Facility facility,
Href project,
List<Href> sshKeys,
Href projectLite,
List<Object> volumes,
List<IpAddress> ipAddresses,
List<ProvisioningEvent> provisioningEvents,
Plan plan,
String rootPassword,
String userdata,
String href
) {
return new AutoValue_Device(id, shortId, hostname, description, state,
tags == null ? ImmutableList.<String> of() : ImmutableList.copyOf(tags),
billingCycle, user, iqn, locked, bondingMode, createdAt, updatedAt, operatingSystem, facility, project,
sshKeys == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(sshKeys),
projectLite,
volumes == null ? ImmutableList.of() : ImmutableList.copyOf(volumes),
ipAddresses == null ? ImmutableList.<IpAddress>of() : ImmutableList.copyOf(ipAddresses),
provisioningEvents == null ? ImmutableList.<ProvisioningEvent> of() : ImmutableList.copyOf(provisioningEvents),
plan,
rootPassword, userdata, href
);
}
Device() {
}
@AutoValue
public abstract static class CreateDevice {
public abstract String hostname();
public abstract String plan();
public abstract String billingCycle();
public abstract String facility();
public abstract Map<String, String> features();
public abstract String operatingSystem();
public abstract Boolean locked();
public abstract String userdata();
public abstract Set<String> tags();
@SerializedNames({"hostname", "plan", "billing_cycle", "facility", "features", "operating_system",
"locked", "userdata", "tags" })
private static CreateDevice create(final String hostname, final String plan, final String billingCycle,
final String facility, final Map<String, String> features, final String operatingSystem,
final Boolean locked, final String userdata,
final Set<String> tags) {
return builder()
.hostname(hostname)
.plan(plan)
.billingCycle(billingCycle)
.facility(facility)
.features(features)
.operatingSystem(operatingSystem)
.locked(locked)
.userdata(userdata)
.tags(tags)
.build();
}
public static Builder builder() {
return new AutoValue_Device_CreateDevice.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder hostname(String hostname);
public abstract Builder plan(String plan);
public abstract Builder billingCycle(String billingCycle);
public abstract Builder facility(String facility);
public abstract Builder features(Map<String, String> features);
public abstract Builder operatingSystem(String operatingSystem);
public abstract Builder locked(Boolean locked);
public abstract Builder userdata(String userdata);
public abstract Builder tags(Set<String> tags);
abstract Map<String, String> features();
abstract Set<String> tags();
abstract CreateDevice autoBuild();
public CreateDevice build() {
return tags(tags() != null ? ImmutableSet.copyOf(tags()) : ImmutableSet.<String> of())
.features(features() != null ? ImmutableMap.copyOf(features()) : ImmutableMap.<String, String> of())
.autoBuild();
}
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.packet.domain;
import java.util.List;
import org.jclouds.compute.domain.OsFamily;
import com.google.common.base.Predicate;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.tryFind;
import static java.util.Arrays.asList;
public enum Distribution {
CENTOS(OsFamily.CENTOS, "centos"),
COREOS(OsFamily.COREOS, "coreos"),
DEBIAN(OsFamily.DEBIAN, "debian"),
UBUNTU(OsFamily.UBUNTU, "ubuntu"),
WINDOWS(OsFamily.WINDOWS, "windows"),
UNRECOGNIZED(OsFamily.UNRECOGNIZED, "");
private static final List<Distribution> values = asList(Distribution.values());
private final OsFamily osFamily;
private final String value;
private Distribution(OsFamily osFamily, String value) {
this.osFamily = checkNotNull(osFamily, "osFamily cannot be null");
this.value = checkNotNull(value, "value cannot be null");
}
public OsFamily osFamily() {
return this.osFamily;
}
public String value() {
return this.value;
}
public static Distribution fromValue(String value) {
return tryFind(values, hasValue(value)).or(UNRECOGNIZED);
}
private static Predicate<Distribution> hasValue(final String value) {
return new Predicate<Distribution>() {
@Override
public boolean apply(Distribution input) {
return input.value.equalsIgnoreCase(value);
}
};
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.packet.domain;
import java.util.List;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@AutoValue
public abstract class Facility {
public abstract String id();
public abstract String name();
public abstract String code();
public abstract List<String> features();
@Nullable
public abstract String address();
@SerializedNames({"id", "name", "code", "features", "address"})
public static Facility create(final String id, String name, String code, List<String> features, String address) {
return new AutoValue_Facility(id, name, code,
features == null ? ImmutableList.<String> of() : ImmutableList.copyOf(features),
address);
}
Facility() {}
}

View File

@ -0,0 +1,34 @@
/*
* 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.packet.domain;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Href {
public abstract String href();
@SerializedNames({ "href" })
public static Href create(String href) {
return new AutoValue_Href(href);
}
Href() {}
}

View File

@ -0,0 +1,59 @@
/*
* 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.packet.domain;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class IpAddress {
public abstract String id();
public abstract Integer addressFamily();
public abstract String netmask();
public abstract Boolean publicAddress();
public abstract Integer cidr();
public abstract Boolean management();
public abstract Boolean manageable();
public abstract Href assignedTo();
public abstract String network();
public abstract String address();
public abstract String gateway();
public abstract String href();
@SerializedNames({"id", "address_family", "netmask", "public", "cidr", "management", "manageable", "assigned_to", "network", "address", "gateway", "href"})
public static IpAddress create(
String id,
Integer addressFamily,
String netmask,
Boolean publicAddress,
Integer cidr,
Boolean management,
Boolean manageable,
Href assignedTo,
String network,
String address,
String gateway,
String href
) {
return new AutoValue_IpAddress(id, addressFamily, netmask, publicAddress, cidr, management, manageable, assignedTo, network, address, gateway, href
);
}
IpAddress() {}
}

View File

@ -0,0 +1,45 @@
/*
* 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.packet.domain;
import java.util.Set;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
@AutoValue
public abstract class OperatingSystem {
public abstract String id();
public abstract String slug();
public abstract String name();
public abstract String distribution();
public abstract String version();
public abstract Set<String> provisionableOn();
@SerializedNames({"id", "slug", "name", "distro", "version", "provisionable_on"})
public static OperatingSystem create(String id, String slug, String name, String distribution, String version, Set<String> provisionableOn) {
return new AutoValue_OperatingSystem(id, slug, name, distribution, version,
provisionableOn == null ? ImmutableSet.<String> of() : ImmutableSet.copyOf(provisionableOn)
);
}
OperatingSystem() {}
}

View File

@ -0,0 +1,50 @@
/*
* 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.packet.domain;
import java.util.List;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@AutoValue
public abstract class Plan {
public abstract String id();
public abstract String slug();
public abstract String name();
public abstract String description();
public abstract String line();
@Nullable
public abstract Specs specs();
public abstract List<Href> availableIn();
public abstract Pricing pricing();
@SerializedNames({"id", "slug", "name", "description", "line", "specs", "available_in", "pricing"})
public static Plan create(final String id, String slug, String name, String description, String line, Specs specs, List<Href> availableIn, Pricing pricing) {
return new AutoValue_Plan(id, slug, name, description, line,
specs,
availableIn == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(availableIn),
pricing
);
}
Plan() {}
}

View File

@ -0,0 +1,34 @@
/*
* 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.packet.domain;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class Pricing {
public abstract double hour();
@SerializedNames({ "hour" })
public static Pricing create(double hour) {
return new AutoValue_Pricing(hour);
}
Pricing() {}
}

View File

@ -0,0 +1,66 @@
/*
* 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.packet.domain;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@AutoValue
public abstract class Project {
public abstract String id();
public abstract String name();
public abstract Date createdAt();
public abstract Date updatedAt();
public abstract Map<String, Object> maxDevices();
public abstract List<Href> members();
public abstract List<Href> memberships();
public abstract List<Href> invitations();
public abstract Href paymentMethod();
public abstract List<Href> devices();
public abstract List<Href> sshKeys();
public abstract List<Href> volumes();
public abstract String href();
@SerializedNames({"id", "name", "created_at", "updated_at", "max_devices", "members", "memberships", "invitations", "payment_method", "devices", "ssh_keys", "volumes", "href"})
public static Project create(String id, String name, Date createdAt, Date updatedAt, Map<String, Object> maxDevices,
List<Href> members, List<Href> memberships, List<Href> invitations, Href paymentMethod,
List<Href> devices,
List<Href> sshKeys,
List<Href> volumes,
String href) {
return new AutoValue_Project(id, name, createdAt, updatedAt,
maxDevices == null ? ImmutableMap.<String, Object> of() : ImmutableMap.copyOf(maxDevices),
members == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(members),
memberships == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(memberships),
invitations == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(invitations),
paymentMethod,
devices == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(devices),
sshKeys == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(sshKeys),
volumes == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(volumes),
href);
}
Project() {}
}

View File

@ -0,0 +1,52 @@
/*
* 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.packet.domain;
import java.util.Date;
import java.util.List;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
@AutoValue
public abstract class ProvisioningEvent {
@Nullable
public abstract String id();
public abstract String type();
public abstract String body();
@Nullable
public abstract Date createdAt();
public abstract List<Href> relationships();
public abstract String interpolated();
@Nullable
public abstract String href();
@SerializedNames({"id", "type", "body", "created_at", "relationships", "interpolated", "href"})
public static ProvisioningEvent create(String id, String type, String body, Date createdAt,
List<Href> relationships, String interpolated, String href) {
return new AutoValue_ProvisioningEvent(id, type, body, createdAt,
relationships == null ? ImmutableList.<Href> of() : ImmutableList.copyOf(relationships),
interpolated,
href);
}
ProvisioningEvent() {}
}

View File

@ -0,0 +1,98 @@
/*
* 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.packet.domain;
import java.util.List;
import java.util.Map;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@AutoValue
public abstract class Specs {
@AutoValue
public abstract static class NIC {
public abstract Integer count();
public abstract String type();
@SerializedNames({ "count", "type" })
public static NIC create(Integer count, String type) {
return new AutoValue_Specs_NIC(count, type);
}
}
@AutoValue
public abstract static class Drive {
public abstract Integer count();
public abstract String size();
public abstract String type();
@SerializedNames({ "count", "size", "type" })
public static Drive create(Integer count, String size, String type) {
return new AutoValue_Specs_Drive(count, size, type);
}
}
@AutoValue
public abstract static class CPU {
public abstract Integer count();
public abstract String type();
@SerializedNames({ "count", "type" })
public static CPU create(Integer count, String type) {
return new AutoValue_Specs_CPU(count, type);
}
}
@AutoValue
public abstract static class Memory {
public abstract String total();
@SerializedNames({ "total" })
public static Memory create(String total) {
return new AutoValue_Specs_Memory(total);
}
}
public abstract List<CPU> cpus();
public abstract Memory memory();
public abstract List<Drive> drives();
public abstract List<NIC> nics();
public abstract Map<String, Object> features();
@SerializedNames({"cpus", "memory", "drives", "nics", "features"})
public static Specs create(List<CPU> cpus, Memory memory, List<Drive> drives, List<NIC> nics, Map<String, Object> features) {
return new AutoValue_Specs(
cpus == null ? ImmutableList.<CPU> of() : ImmutableList.copyOf(cpus),
memory,
drives == null ? ImmutableList.<Drive> of() : ImmutableList.copyOf(drives),
nics == null ? ImmutableList.<NIC> of() : ImmutableList.copyOf(nics),
features == null ? ImmutableMap.<String, Object> of() : ImmutableMap.copyOf(features)
);
}
Specs() {}
}

View File

@ -0,0 +1,55 @@
/*
* 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.packet.domain;
import java.util.Date;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class SshKey {
@AutoValue
public abstract static class Owner {
public abstract String href();
@SerializedNames({ "href" })
public static Owner create(String href) {
return new AutoValue_SshKey_Owner(href);
}
}
public abstract String id();
public abstract String label();
public abstract String key();
public abstract String fingerprint();
public abstract Date createdAt();
public abstract Date updatedAt();
@Nullable public abstract Owner owner();
public abstract String href();
@SerializedNames({"id", "label", "key", "fingerprint", "created_at", "updated_at", "owner", "href"})
public static SshKey create(String id, String label, String key, String fingerprint, Date createdAt, Date updatedAt, Owner owner, String href) {
return new AutoValue_SshKey(id, label, key, fingerprint, createdAt, updatedAt, owner, href);
}
SshKey() {}
}

View File

@ -0,0 +1,84 @@
/*
* 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.packet.domain.internal;
import java.util.Iterator;
import java.util.List;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.SerializedNames;
import org.jclouds.packet.domain.Href;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Base class for all collections that return paginated results.
*/
public abstract class PaginatedCollection<T> extends IterableWithMarker<T> {
@AutoValue
public abstract static class Meta {
public abstract long total();
@Nullable public abstract Href first();
@Nullable public abstract Href previous();
@Nullable public abstract Href self();
@Nullable public abstract Href next();
@Nullable public abstract Href last();
@SerializedNames({ "total", "first", "previous", "self", "next", "last" })
public static Meta create(long total, Href first, Href previous, Href self, Href next, Href last) {
return new AutoValue_PaginatedCollection_Meta(total, first, previous, self, next, last);
}
Meta() { }
}
private final List<T> items;
private final Meta meta;
protected PaginatedCollection(List<T> items, Meta meta) {
this.items = ImmutableList.copyOf(checkNotNull(items, "items cannot be null"));
this.meta = meta;
}
public List<T> items() {
return items;
}
public Meta meta() {
return meta;
}
@Override
public Iterator<T> iterator() {
return items.iterator();
}
@Override
public Optional<Object> nextMarker() {
if (meta == null || meta.next() == null) {
return Optional.absent();
}
return Optional.fromNullable((Object) meta.next());
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.packet.domain.options;
import org.jclouds.http.options.BaseHttpRequestOptions;
/**
* Options to customize how paginated lists are returned.
*/
public class ListOptions extends BaseHttpRequestOptions {
public static final String PAGE_PARAM = "page";
public static final String PER_PAGE_PARAM = "per_page";
/**
* Configures the number of entries to return in each page.
*/
public ListOptions perPage(int perPage) {
queryParameters.put(PER_PAGE_PARAM, String.valueOf(perPage));
return this;
}
/**
* Configures the number of the page to be returned.
*/
public ListOptions page(int page) {
queryParameters.put(PAGE_PARAM, String.valueOf(page));
return this;
}
public static final class Builder {
/**
* @see {@link ListOptions#perPage(int)}
*/
public static ListOptions perPage(int perPage) {
return new ListOptions().perPage(perPage);
}
/**
* @see {@link ListOptions#page(int)}
*/
public static ListOptions page(int page) {
return new ListOptions().page(page);
}
}
}

View File

@ -0,0 +1,163 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.collect.internal.Arg0ToPagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.Payload;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({AddXAuthTokenToRequest.class, AddApiVersionToRequest.class})
public interface DeviceApi {
@Named("device:list")
@GET
@Path("/projects/{projectId}/devices")
@ResponseParser(ParseDevices.class)
@Transform(ParseDevices.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<Device> list();
@Named("device:list")
@GET
@Path("/projects/{projectId}/devices")
@ResponseParser(ParseDevices.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<Device> list(ListOptions options);
final class ParseDevices extends ParseJson<ParseDevices.Devices> {
@Inject
ParseDevices(Json json) {
super(json, TypeLiteral.get(Devices.class));
}
private static class Devices extends PaginatedCollection<Device> {
@ConstructorProperties({"devices", "meta"})
public Devices(List<Device> items, Meta meta) {
super(items, meta);
}
}
public static class ToPagedIterable extends Arg0ToPagedIterable.FromCaller<Device, ToPagedIterable> {
private final PacketApi api;
private final Function<Href, ListOptions> hrefToOptions;
@Inject
ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
this.api = api;
this.hrefToOptions = hrefToOptions;
}
@Override
protected Function<Object, IterableWithMarker<Device>> markerToNextForArg0(Optional<Object> arg0) {
String projectId = arg0.get().toString();
final DeviceApi deviceApi = api.deviceApi(projectId);
return new Function<Object, IterableWithMarker<Device>>() {
@SuppressWarnings("unchecked")
@Override
public IterableWithMarker<Device> apply(Object input) {
ListOptions listOptions = hrefToOptions.apply(Href.class.cast(input));
return IterableWithMarker.class.cast(deviceApi.list(listOptions));
}
};
}
}
}
@Named("device:create")
@POST
@Path("/projects/{projectId}/devices")
@Produces(MediaType.APPLICATION_JSON)
Device create(@BinderParam(BindToJsonPayload.class) Device.CreateDevice device);
@Named("device:get")
@GET
@Path("/devices/{id}")
@Fallback(NullOnNotFoundOr404.class)
@Nullable
Device get(@PathParam("id") String id);
@Named("device:delete")
@DELETE
@Path("/devices/{id}")
@Fallback(VoidOnNotFoundOr404.class)
void delete(@PathParam("id") String id);
@Named("device:powerOff")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("/devices/{id}/actions")
@Payload("{\"type\":\"power_off\"}")
void powerOff(@PathParam("id") String id);
@Named("device:powerOn")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("/devices/{id}/actions")
@Payload("{\"type\":\"power_on\"}")
void powerOn(@PathParam("id") String id);
@Named("device:reboot")
@POST
@Produces(MediaType.APPLICATION_JSON)
@Path("/devices/{id}/actions")
@Payload("{\"type\":\"reboot\"}")
void reboot(@PathParam("id") String id);
}

View File

@ -0,0 +1,94 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Facility;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.packet.functions.BaseToPagedIterable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Path("/facilities")
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({ AddXAuthTokenToRequest.class, AddApiVersionToRequest.class} )
public interface FacilityApi {
@Named("facility:list")
@GET
@ResponseParser(ParseFacilities.class)
@Transform(ParseFacilities.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<Facility> list();
@Named("facility:list")
@GET
@ResponseParser(ParseFacilities.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<Facility> list(ListOptions options);
final class ParseFacilities extends ParseJson<ParseFacilities.Facilities> {
@Inject
ParseFacilities(Json json) {
super(json, TypeLiteral.get(Facilities.class));
}
private static class Facilities extends PaginatedCollection<Facility> {
@ConstructorProperties({ "facilities", "meta" })
public Facilities(List<Facility> items, Meta meta) {
super(items, meta);
}
}
private static class ToPagedIterable extends BaseToPagedIterable<Facility, ListOptions> {
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
super(api, hrefToOptions);
}
@Override
protected IterableWithMarker<Facility> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
return api.facilityApi().list(options);
}
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.OperatingSystem;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.packet.functions.BaseToPagedIterable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Path("/operating-systems")
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({ AddXAuthTokenToRequest.class, AddApiVersionToRequest.class} )
public interface OperatingSystemApi {
@Named("operatingsystem:list")
@GET
@ResponseParser(ParseOperatingSystems.class)
@Transform(ParseOperatingSystems.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<OperatingSystem> list();
@Named("operatingsystem:list")
@GET
@ResponseParser(ParseOperatingSystems.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<OperatingSystem> list(ListOptions options);
final class ParseOperatingSystems extends ParseJson<ParseOperatingSystems.OperatingSystems> {
@Inject
ParseOperatingSystems(Json json) {
super(json, TypeLiteral.get(ParseOperatingSystems.OperatingSystems.class));
}
private static class OperatingSystems extends PaginatedCollection<OperatingSystem> {
@ConstructorProperties({ "operating_systems", "meta" })
public OperatingSystems(List<OperatingSystem> items, Meta meta) {
super(items, meta);
}
}
private static class ToPagedIterable extends BaseToPagedIterable<OperatingSystem, ListOptions> {
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
super(api, hrefToOptions);
}
@Override
protected IterableWithMarker<OperatingSystem> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
return api.operatingSystemApi().list(options);
}
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.Plan;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.packet.functions.BaseToPagedIterable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Path("/plans")
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({ AddXAuthTokenToRequest.class, AddApiVersionToRequest.class} )
public interface PlanApi {
@Named("plan:list")
@GET
@ResponseParser(ParsePlans.class)
@Transform(ParsePlans.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<Plan> list();
@Named("plan:list")
@GET
@ResponseParser(ParsePlans.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<Plan> list(ListOptions options);
final class ParsePlans extends ParseJson<ParsePlans.Plans> {
@Inject
ParsePlans(Json json) {
super(json, TypeLiteral.get(ParsePlans.Plans.class));
}
private static class Plans extends PaginatedCollection<Plan> {
@ConstructorProperties({ "plans", "meta" })
public Plans(List<Plan> items, Meta meta) {
super(items, meta);
}
}
private static class ToPagedIterable extends BaseToPagedIterable<Plan, ListOptions> {
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
super(api, hrefToOptions);
}
@Override
protected IterableWithMarker<Plan> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
return api.planApi().list(options);
}
}
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.Project;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.packet.functions.BaseToPagedIterable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Path("/projects")
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({ AddXAuthTokenToRequest.class, AddApiVersionToRequest.class} )
public interface ProjectApi {
@Named("project:list")
@GET
@ResponseParser(ParseProjects.class)
@Transform(ParseProjects.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<Project> list();
@Named("project:list")
@GET
@ResponseParser(ParseProjects.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<Project> list(ListOptions options);
final class ParseProjects extends ParseJson<ParseProjects.Projects> {
@Inject
ParseProjects(Json json) {
super(json, TypeLiteral.get(Projects.class));
}
private static class Projects extends PaginatedCollection<Project> {
@ConstructorProperties({ "projects", "meta" })
public Projects(List<Project> items, Meta meta) {
super(items, meta);
}
}
private static class ToPagedIterable extends BaseToPagedIterable<Project, ListOptions> {
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
super(api, hrefToOptions);
}
@Override
protected IterableWithMarker<Project> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
return api.projectApi().list(options);
}
}
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.packet.features;
import java.beans.ConstructorProperties;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.Fallbacks;
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.PagedIterable;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.SshKey;
import org.jclouds.packet.domain.internal.PaginatedCollection;
import org.jclouds.packet.domain.options.ListOptions;
import org.jclouds.packet.filters.AddApiVersionToRequest;
import org.jclouds.packet.filters.AddXAuthTokenToRequest;
import org.jclouds.packet.functions.BaseToPagedIterable;
import org.jclouds.rest.annotations.Fallback;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.PayloadParam;
import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.binders.BindToJsonPayload;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.inject.TypeLiteral;
@Path("/ssh-keys")
@Consumes(MediaType.APPLICATION_JSON)
@RequestFilters({ AddXAuthTokenToRequest.class, AddApiVersionToRequest.class} )
public interface SshKeyApi {
@Named("sshkey:list")
@GET
@ResponseParser(ParseSshKeys.class)
@Transform(ParseSshKeys.ToPagedIterable.class)
@Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
PagedIterable<SshKey> list();
@Named("sshkey:list")
@GET
@ResponseParser(ParseSshKeys.class)
@Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
IterableWithMarker<SshKey> list(ListOptions options);
final class ParseSshKeys extends ParseJson<ParseSshKeys.SshKeys> {
@Inject
ParseSshKeys(Json json) {
super(json, TypeLiteral.get(ParseSshKeys.SshKeys.class));
}
private static class SshKeys extends PaginatedCollection<SshKey> {
@ConstructorProperties({ "ssh_keys", "meta" })
public SshKeys(List<SshKey> items, Meta meta) {
super(items, meta);
}
}
private static class ToPagedIterable extends BaseToPagedIterable<SshKey, ListOptions> {
@Inject ToPagedIterable(PacketApi api, Function<Href, ListOptions> hrefToOptions) {
super(api, hrefToOptions);
}
@Override
protected IterableWithMarker<SshKey> fetchPageUsingOptions(ListOptions options, Optional<Object> arg0) {
return api.sshKeyApi().list(options);
}
}
}
@Named("sshkey:create")
@POST
@Produces(MediaType.APPLICATION_JSON)
@MapBinder(BindToJsonPayload.class)
SshKey create(@PayloadParam("label") String label, @PayloadParam("key") String key);
@Named("sshkey:get")
@GET
@Path("/{id}")
@Fallback(NullOnNotFoundOr404.class)
@Nullable
SshKey get(@PathParam("id") String id);
@Named("sshkey:delete")
@DELETE
@Path("/{id}")
@Fallback(VoidOnNotFoundOr404.class)
void delete(@PathParam("id") String id);
}

View File

@ -0,0 +1,57 @@
/*
* 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.packet.filters;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.rest.annotations.ApiVersion;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.HttpHeaders.ACCEPT;
import static java.lang.String.format;
@Singleton
public class AddApiVersionToRequest implements HttpRequestFilter {
private final String apiVersion;
@Inject
AddApiVersionToRequest(@ApiVersion String apiVersion) {
this.apiVersion = apiVersion;
}
@Override
public HttpRequest filter(final HttpRequest request) throws HttpException {
Collection<String> accept = checkNotNull(request.getHeaders().get(ACCEPT), "accept header must not be null");
String versionHeader = Joiner.on("; ").join(ImmutableList.builder()
.addAll(accept)
.add(format("version=%s", apiVersion))
.build());
return request.toBuilder()
.replaceHeader(ACCEPT, versionHeader)
.build();
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.packet.filters;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.location.Provider;
import com.google.common.base.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
@Singleton
public class AddXAuthTokenToRequest implements HttpRequestFilter {
private final Supplier<Credentials> creds;
@Inject
AddXAuthTokenToRequest(@Provider Supplier<Credentials> creds) {
this.creds = creds;
}
@Override
public HttpRequest filter(HttpRequest request) throws HttpException {
Credentials currentCreds = checkNotNull(creds.get(), "credential supplier returned null");
return request.toBuilder().replaceHeader("X-Auth-Token", currentCreds.credential).build();
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.packet.functions;
import javax.inject.Inject;
import org.jclouds.collect.IterableWithMarker;
import org.jclouds.collect.internal.Arg0ToPagedIterable;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.options.ListOptions;
import com.google.common.base.Function;
import com.google.common.base.Optional;
/**
* Base class to implement the functions that build the
* <code>PagedIterable</code>. Subclasses just need to override the
* {@link #fetchPageUsingOptions(ListOptions, Optional)} to invoke the right API
* method with the given options parameter to get the next page.
*/
public abstract class BaseToPagedIterable<T, O extends ListOptions> extends
Arg0ToPagedIterable<T, BaseToPagedIterable<T, O>> {
private final Function<Href, O> hrefToOptions;
protected final PacketApi api;
@Inject protected BaseToPagedIterable(PacketApi api, Function<Href, O> hrefToOptions) {
this.api = api;
this.hrefToOptions = hrefToOptions;
}
protected abstract IterableWithMarker<T> fetchPageUsingOptions(O options, Optional<Object> arg0);
@Override
protected Function<Object, IterableWithMarker<T>> markerToNextForArg0(final Optional<Object> arg0) {
return new Function<Object, IterableWithMarker<T>>() {
@Override
public IterableWithMarker<T> apply(Object input) {
O nextOptions = hrefToOptions.apply(Href.class.cast(input));
return fetchPageUsingOptions(nextOptions, arg0);
}
};
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.packet.functions;
import java.net.URI;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.options.ListOptions;
import com.google.common.base.Function;
import com.google.common.collect.Multimap;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.Iterables.getFirst;
import static org.jclouds.http.utils.Queries.queryParser;
import static org.jclouds.packet.domain.options.ListOptions.PAGE_PARAM;
import static org.jclouds.packet.domain.options.ListOptions.PER_PAGE_PARAM;
/**
* Transforms an href returned by the API into a {@link ListOptions} that can be
* used to perform a request to get another page of a paginated list.
*/
public class HrefToListOptions implements Function<Href, ListOptions> {
@Override
public ListOptions apply(Href input) {
checkNotNull(input, "input cannot be null");
Multimap<String, String> queryParams = queryParser().apply(URI.create(input.href()).getQuery());
String nextPage = getFirstOrNull(PAGE_PARAM, queryParams);
String nextPerPage = getFirstOrNull(PER_PAGE_PARAM, queryParams);
ListOptions options = new ListOptions();
if (nextPage != null) {
options.page(Integer.parseInt(nextPage));
}
if (nextPerPage != null) {
options.perPage(Integer.parseInt(nextPerPage));
}
return options;
}
public static String getFirstOrNull(String key, Multimap<String, String> params) {
return params.containsKey(key) ? emptyToNull(getFirst(params.get(key), null)) : null;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.packet.handlers;
import javax.inject.Singleton;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
/**
* This will parse and set an appropriate exception on the command object.
*/
@Singleton
public class PacketErrorHandler implements HttpErrorHandler {
public void handleError(HttpCommand command, HttpResponse response) {
// it is important to always read fully and close streams
byte[] data = closeClientButKeepContentStream(response);
String message = data != null ? new String(data) : null;
Exception exception = message != null ? new HttpResponseException(command, response, message)
: new HttpResponseException(command, response);
message = message != null ? message : String.format("%s -> %s", command.getCurrentRequest().getRequestLine(),
response.getStatusLine());
switch (response.getStatusCode()) {
case 400:
exception = new IllegalArgumentException(message, exception);
break;
case 401:
case 403:
exception = new AuthorizationException(message, exception);
break;
case 404:
if (!command.getCurrentRequest().getMethod().equals("DELETE")) {
exception = new ResourceNotFoundException(message, exception);
}
break;
case 409:
exception = new IllegalStateException(message, exception);
break;
}
command.setException(exception);
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.packet;
import org.jclouds.providers.internal.BaseProviderMetadataTest;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "PacketProviderMetadataTest")
public class PacketProviderMetadataTest extends BaseProviderMetadataTest {
public PacketProviderMetadataTest() {
super(new PacketProviderMetadata(), new PacketApiMetadata());
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.packet.compute;
import org.jclouds.packet.PacketApiMetadata;
import org.jclouds.packet.PacketProviderMetadata;
import org.jclouds.providers.internal.BaseProviderMetadataTest;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "PacketComputeProviderMetadataTest")
public class PacketComputeProviderMetadataTest extends BaseProviderMetadataTest {
public PacketComputeProviderMetadataTest() {
super(new PacketProviderMetadata(), new PacketApiMetadata());
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.packet.compute;
import java.util.Properties;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
/**
* Live tests for the {@link org.jclouds.compute.ComputeService} integration.
*/
@Test(groups = "live", singleThreaded = true, testName = "PacketComputeServiceLiveTest")
public class PacketComputeServiceLiveTest extends BaseComputeServiceLiveTest {
public PacketComputeServiceLiveTest() {
provider = "packet";
}
@Override
protected Module getSshModule() {
return new SshjSshClientModule();
}
@Override
@Test(expectedExceptions = AuthorizationException.class)
public void testCorrectAuthException() throws Exception {
ComputeServiceContext context = null;
try {
Properties overrides = setupProperties();
overrides.setProperty(provider + ".identity", "MOM:MA");
overrides.setProperty(provider + ".credential", "MIA");
context = newBuilder()
.modules(ImmutableSet.of(getLoggingModule(), credentialStoreModule))
.overrides(overrides)
.build(ComputeServiceContext.class);
// replace listNodes with listImages as it doesn't require `projectId`
context.getComputeService().listImages();
} catch (AuthorizationException e) {
throw e;
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
} finally {
if (context != null)
context.close();
}
}
@Override
public void testOptionToNotBlock() throws Exception {
// Packet ComputeService implementation has to block until the node
// is provisioned, to be able to return it.
}
@Override
protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap<String, String> userMetadata) {
// The Packet API does not return the user data
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.packet.compute;
import java.io.IOException;
import java.util.Set;
import org.jclouds.compute.domain.OsFamily;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.internal.BaseTemplateBuilderLiveTest;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
import static org.testng.Assert.assertEquals;
@Test(groups = "live", testName = "PacketTemplateBuilderLiveTest")
public class PacketTemplateBuilderLiveTest extends BaseTemplateBuilderLiveTest {
public PacketTemplateBuilderLiveTest() {
provider = "packet";
}
@Test
@Override
public void testDefaultTemplateBuilder() throws IOException {
Template defaultTemplate = view.getComputeService().templateBuilder().build();
assert defaultTemplate.getImage().getOperatingSystem().getVersion().startsWith("16.") : defaultTemplate
.getImage().getOperatingSystem().getVersion();
assertEquals(defaultTemplate.getImage().getOperatingSystem().is64Bit(), true);
assertEquals(defaultTemplate.getImage().getOperatingSystem().getFamily(), OsFamily.UBUNTU);
assertEquals(getCores(defaultTemplate.getHardware()), 1.0d);
}
@Override
protected Set<String> getIso3166Codes() {
return ImmutableSet.of("US-CA", "US-NJ", "NL", "JP");
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.packet.compute.internal;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.jclouds.apis.BaseApiLiveTest;
import org.jclouds.compute.config.ComputeServiceProperties;
import org.jclouds.packet.PacketApi;
import com.google.common.base.Predicate;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
import static org.testng.Assert.assertTrue;
public class BasePacketApiLiveTest extends BaseApiLiveTest<PacketApi> {
private Predicate<String> deviceRunning;
private Predicate<String> deviceSuspended;
private Predicate<String> deviceTerminated;
public BasePacketApiLiveTest() {
provider = "packet";
}
@Override
protected Properties setupProperties() {
Properties props = super.setupProperties();
props.put(ComputeServiceProperties.POLL_INITIAL_PERIOD, 1000);
props.put(ComputeServiceProperties.POLL_MAX_PERIOD, 10000);
props.put(ComputeServiceProperties.TIMEOUT_IMAGE_AVAILABLE, TimeUnit.MINUTES.toMillis(45));
return props;
}
@Override
protected PacketApi create(Properties props, Iterable<Module> modules) {
Injector injector = newBuilder().modules(modules).overrides(props).buildInjector();
deviceRunning = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>(){},
Names.named(TIMEOUT_NODE_RUNNING)));
deviceSuspended = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>(){},
Names.named(TIMEOUT_NODE_SUSPENDED)));
deviceTerminated = injector.getInstance(Key.get(new TypeLiteral<Predicate<String>>(){},
Names.named(TIMEOUT_NODE_TERMINATED)));
return injector.getInstance(PacketApi.class);
}
protected void assertNodeRunning(String deviceId) {
assertTrue(deviceRunning.apply(deviceId), String.format("Device %s did not start in the configured timeout", deviceId));
}
protected void assertNodeSuspended(String deviceId) {
assertTrue(deviceSuspended.apply(deviceId), String.format("Device %s was not suspended in the configured timeout", deviceId));
}
protected void assertNodeTerminated(String deviceId) {
assertTrue(deviceTerminated.apply(deviceId), String.format("Device %s was not terminated in the configured timeout", deviceId));
}
}

View File

@ -0,0 +1,146 @@
/*
* 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.packet.compute.internal;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.jclouds.ContextBuilder;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.json.Json;
import org.jclouds.packet.PacketApi;
import org.jclouds.packet.PacketProviderMetadata;
import org.jclouds.rest.ApiContext;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import com.google.common.base.Charsets;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonParser;
import com.google.inject.Module;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
import static org.testng.Assert.assertEquals;
public class BasePacketApiMockTest {
private static final String X_AUTHORIZATION_TOKEN = "c5401990f0c24135e8d6b5d260603fc71696d4738da9aa04a720229a01a2521d";
private static final String DEFAULT_ENDPOINT = new PacketProviderMetadata().getEndpoint();
private final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor()));
protected MockWebServer server;
protected PacketApi api;
private Json json;
private ApiContext<PacketApi> ctx;
// So that we can ignore formatting.
private final JsonParser parser = new JsonParser();
@BeforeMethod
public void start() throws IOException {
server = new MockWebServer();
server.play();
ctx = ContextBuilder.newBuilder("packet")
.credentials("", X_AUTHORIZATION_TOKEN)
.endpoint(url(""))
.modules(modules)
.overrides(overrides())
.build();
json = ctx.utils().injector().getInstance(Json.class);
api = ctx.getApi();
}
@AfterMethod(alwaysRun = true)
public void stop() throws IOException {
server.shutdown();
api.close();
}
protected Properties overrides() {
Properties properties = new Properties();
properties.put(PROPERTY_MAX_RETRIES, "0"); // Do not retry
return properties;
}
protected String url(String path) {
return server.getUrl(path).toString();
}
protected MockResponse jsonResponse(String resource) {
return new MockResponse().addHeader("Content-Type", "application/json").setBody(stringFromResource(resource));
}
protected MockResponse response404() {
return new MockResponse().setStatus("HTTP/1.1 404 Not Found");
}
protected MockResponse response204() {
return new MockResponse().setStatus("HTTP/1.1 204 No Content");
}
protected String stringFromResource(String resourceName) {
try {
return Resources.toString(getClass().getResource(resourceName), Charsets.UTF_8)
.replace(DEFAULT_ENDPOINT, url(""));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
protected <T> T onlyObjectFromResource(String resourceName, TypeToken<Map<String, T>> type) {
// Assume JSON objects passed here will be in the form: { "entity": { ... } }
String text = stringFromResource(resourceName);
Map<String, T> object = json.fromJson(text, type.getType());
checkArgument(!object.isEmpty(), "The given json does not contain any object: %s", text);
checkArgument(object.keySet().size() == 1, "The given json does not contain more than one object: %s", text);
return object.get(getOnlyElement(object.keySet()));
}
protected <T> T objectFromResource(String resourceName, Class<T> type) {
String text = stringFromResource(resourceName);
return json.fromJson(text, type);
}
protected RecordedRequest assertSent(MockWebServer server, String method, String path) throws InterruptedException {
RecordedRequest request = server.takeRequest();
assertEquals(request.getMethod(), method);
assertEquals(request.getPath(), path);
assertEquals(request.getHeader("Accept"), "application/json; version=" + ctx.getMetadata().get("apiVersion"));
assertEquals(request.getHeader("X-Auth-Token"), X_AUTHORIZATION_TOKEN);
return request;
}
protected RecordedRequest assertSent(MockWebServer server, String method, String path, String json)
throws InterruptedException {
RecordedRequest request = assertSent(server, method, path);
assertEquals(request.getHeader("Content-Type"), "application/json");
assertEquals(parser.parse(new String(request.getBody(), Charsets.UTF_8)), parser.parse(json));
return request;
}
}

View File

@ -0,0 +1,137 @@
/*
* 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.packet.features;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.BillingCycle;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.SshKey;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", singleThreaded = true, testName = "DeviceApiLiveTest")
public class DeviceApiLiveTest extends BasePacketApiLiveTest {
private SshKey sshKey;
private String deviceId;
@BeforeClass
public void setupDevice() {
Map<String, String> keyPair = SshKeys.generate();
sshKey = api.sshKeyApi().create(prefix + "-device-livetest", keyPair.get("public"));
}
@AfterClass(alwaysRun = true)
public void tearDown() {
if (sshKey != null) {
api.sshKeyApi().delete(sshKey.id());
}
}
public void testCreate() {
Device deviceCreated = api().create(
Device.CreateDevice.builder()
.hostname(prefix + "-device-livetest")
.plan("baremetal_0")
.billingCycle(BillingCycle.HOURLY.value())
.facility("ewr1")
.features(ImmutableMap.<String, String>of())
.operatingSystem("ubuntu_16_04")
.locked(false)
.userdata("")
.tags(ImmutableSet.<String> of())
.build()
);
deviceId = deviceCreated.id();
assertNodeRunning(deviceId);
Device device = api().get(deviceId);
assertNotNull(device, "Device must not be null");
}
@Test(groups = "live", dependsOnMethods = "testCreate")
public void testReboot() {
api().reboot(deviceId);
assertNodeRunning(deviceId);
}
@Test(groups = "live", dependsOnMethods = "testReboot")
public void testPowerOff() {
api().powerOff(deviceId);
assertNodeSuspended(deviceId);
}
@Test(groups = "live", dependsOnMethods = "testPowerOff")
public void testPowerOn() {
api().powerOn(deviceId);
assertNodeRunning(deviceId);
}
@Test(dependsOnMethods = "testCreate")
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<Device>() {
@Override
public boolean apply(Device input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All devices must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some devices to be returned");
}
@Test(dependsOnMethods = "testCreate")
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<Device>() {
@Override
public boolean apply(Device input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All devices must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some devices to be returned");
}
@Test(dependsOnMethods = "testPowerOn", alwaysRun = true)
public void testDelete() throws InterruptedException {
if (deviceId != null) {
api().delete(deviceId);
assertNodeTerminated(deviceId);
assertNull(api().get(deviceId));
}
}
private DeviceApi api() {
return api.deviceApi(identity);
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.BillingCycle;
import org.jclouds.packet.domain.Device;
import org.jclouds.packet.domain.Device.CreateDevice;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "DeviceApiMockTest", singleThreaded = true)
public class DeviceApiMockTest extends BasePacketApiMockTest {
public void testListDevices() throws InterruptedException {
server.enqueue(jsonResponse("/devices-first.json"));
server.enqueue(jsonResponse("/devices-last.json"));
Iterable<Device> devices = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").list().concat();
assertEquals(size(devices), 7); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices");
assertSent(server, "GET", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=2");
}
public void testListDevicesReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Device> devices = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").list().concat();
assertTrue(isEmpty(devices));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices");
}
public void testListDevicesWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/devices-first.json"));
Iterable<Device> devices = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").list(page(1).perPage(5));
assertEquals(size(devices), 5);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1&per_page=5");
}
public void testListDevicesWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Device> actions = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").list(page(1).perPage(5));
assertTrue(isEmpty(actions));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1&per_page=5");
}
public void testGetDevice() throws InterruptedException {
server.enqueue(jsonResponse("/device.json"));
Device device = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").get("1");
assertEquals(device, objectFromResource("/device.json", Device.class));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/devices/1");
}
public void testGetDeviceReturns404() throws InterruptedException {
server.enqueue(response404());
Device device = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").get("1");
assertNull(device);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/devices/1");
}
public void testCreateDevice() throws InterruptedException {
server.enqueue(jsonResponse("/device-create-res.json"));
Device device = api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").create(
CreateDevice.builder()
.hostname("jclouds-device-livetest")
.plan("baremetal_0")
.billingCycle(BillingCycle.HOURLY.value())
.facility("ewr1")
.features(ImmutableMap.<String, String>of())
.operatingSystem("ubuntu_16_04")
.locked(false)
.userdata("")
.tags(ImmutableSet.<String> of())
.build()
);
assertEquals(device, objectFromResource("/device-create-res.json", Device.class));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "POST", "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices", stringFromResource("/device-create-req.json"));
}
public void testDeleteDevice() throws InterruptedException {
server.enqueue(response204());
api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").delete("1");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "DELETE", "/devices/1");
}
public void testDeleteDeviceReturns404() throws InterruptedException {
server.enqueue(response404());
api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").delete("1");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "DELETE", "/devices/1");
}
public void testActionPowerOn() throws InterruptedException {
server.enqueue(jsonResponse("/power-on.json"));
api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").powerOn("deviceId");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "POST", "/devices/deviceId/actions");
}
public void testActionPowerOff() throws InterruptedException {
server.enqueue(jsonResponse("/power-off.json"));
api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").powerOff("deviceId");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "POST", "/devices/deviceId/actions");
}
public void testActionReboot() throws InterruptedException {
server.enqueue(jsonResponse("/reboot.json"));
api.deviceApi("93907f48-adfe-43ed-ad89-0e6e83721a54").reboot("deviceId");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "POST", "/devices/deviceId/actions");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.packet.features;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.Facility;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", testName = "FacilityApiLiveTest")
public class FacilityApiLiveTest extends BasePacketApiLiveTest {
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<Facility>() {
@Override
public boolean apply(Facility input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All facilities must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some facilities to be returned");
}
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<Facility>() {
@Override
public boolean apply(Facility input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All facilities must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some facilities to be returned");
}
private FacilityApi api() {
return api.facilityApi();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.Facility;
import org.testng.annotations.Test;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "FacilityApiMockTest", singleThreaded = true)
public class FacilityApiMockTest extends BasePacketApiMockTest {
public void testListFacilities() throws InterruptedException {
server.enqueue(jsonResponse("/facilities-first.json"));
server.enqueue(jsonResponse("/facilities-last.json"));
Iterable<Facility> facilities = api.facilityApi().list().concat();
assertEquals(size(facilities), 3); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/facilities");
assertSent(server, "GET", "/facilities?page=2");
}
public void testListFacilitiesReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Facility> facilities = api.facilityApi().list().concat();
assertTrue(isEmpty(facilities));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/facilities");
}
public void testListFacilitiesWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/facilities-first.json"));
Iterable<Facility> actions = api.facilityApi().list(page(1).perPage(2));
assertEquals(size(actions), 2);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/facilities?page=1&per_page=2");
}
public void testListFacilitiesWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Facility> actions = api.facilityApi().list(page(1).perPage(2));
assertTrue(isEmpty(actions));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/facilities?page=1&per_page=2");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.packet.features;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.OperatingSystem;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", testName = "OperatingSystemApiLiveTest")
public class OperatingSystemApiLiveTest extends BasePacketApiLiveTest {
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<OperatingSystem>() {
@Override
public boolean apply(OperatingSystem input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All operating systems must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some operating systems to be returned");
}
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<OperatingSystem>() {
@Override
public boolean apply(OperatingSystem input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All operating systems must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some operating systems to be returned");
}
private OperatingSystemApi api() {
return api.operatingSystemApi();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.OperatingSystem;
import org.testng.annotations.Test;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "OperatingSystemApiMockTest", singleThreaded = true)
public class OperatingSystemApiMockTest extends BasePacketApiMockTest {
public void testListOperatingSystems() throws InterruptedException {
server.enqueue(jsonResponse("/operatingSystems-first.json"));
server.enqueue(jsonResponse("/operatingSystems-last.json"));
Iterable<OperatingSystem> operatingSystems = api.operatingSystemApi().list().concat();
assertEquals(size(operatingSystems), 14); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/operating-systems");
assertSent(server, "GET", "/operating-systems?page=2");
}
public void testListOperatingSystemsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<OperatingSystem> operatingSystems = api.operatingSystemApi().list().concat();
assertTrue(isEmpty(operatingSystems));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/operating-systems");
}
public void testListOperatingSystemsWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/operatingSystems-first.json"));
Iterable<OperatingSystem> operatingSystems = api.operatingSystemApi().list(page(1).perPage(5));
assertEquals(size(operatingSystems), 7);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/operating-systems?page=1&per_page=5");
}
public void testListOperatingSystemsWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<OperatingSystem> operatingSystems = api.operatingSystemApi().list(page(1).perPage(5));
assertTrue(isEmpty(operatingSystems));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/operating-systems?page=1&per_page=5");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.packet.features;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.Plan;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", testName = "PlanApiLiveTest")
public class PlanApiLiveTest extends BasePacketApiLiveTest {
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<Plan>() {
@Override
public boolean apply(Plan input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All plans must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some plans to be returned");
}
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<Plan>() {
@Override
public boolean apply(Plan input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All plans must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some plans to be returned");
}
private PlanApi api() {
return api.planApi();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.Plan;
import org.testng.annotations.Test;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "PlanApiMockTest", singleThreaded = true)
public class PlanApiMockTest extends BasePacketApiMockTest {
public void testListPlans() throws InterruptedException {
server.enqueue(jsonResponse("/plans-first.json"));
server.enqueue(jsonResponse("/plans-last.json"));
Iterable<Plan> plans = api.planApi().list().concat();
assertEquals(size(plans), 7); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/plans");
assertSent(server, "GET", "/plans?page=2");
}
public void testListPlansReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Plan> plans = api.planApi().list().concat();
assertTrue(isEmpty(plans));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/plans");
}
public void testListPlansWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/plans-first.json"));
Iterable<Plan> plans = api.planApi().list(page(1).perPage(5));
assertEquals(size(plans), 4);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/plans?page=1&per_page=5");
}
public void testListPlansWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Plan> plans = api.planApi().list(page(1).perPage(5));
assertTrue(isEmpty(plans));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/plans?page=1&per_page=5");
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.packet.features;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.Project;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", testName = "ProjectApiLiveTest")
public class ProjectApiLiveTest extends BasePacketApiLiveTest {
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<Project>() {
@Override
public boolean apply(Project input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All projects must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some projects to be returned");
}
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<Project>() {
@Override
public boolean apply(Project input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All projects must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some projects to be returned");
}
private ProjectApi api() {
return api.projectApi();
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.Project;
import org.testng.annotations.Test;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "ProjectApiMockTest", singleThreaded = true)
public class ProjectApiMockTest extends BasePacketApiMockTest {
public void testListProjects() throws InterruptedException {
server.enqueue(jsonResponse("/projects-first.json"));
server.enqueue(jsonResponse("/projects-last.json"));
Iterable<Project> projects = api.projectApi().list().concat();
assertEquals(size(projects), 8); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/projects");
assertSent(server, "GET", "/projects?page=2");
}
public void testListProjectsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Project> projects = api.projectApi().list().concat();
assertTrue(isEmpty(projects));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects");
}
public void testListProjectsWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/projects-first.json"));
Iterable<Project> actions = api.projectApi().list(page(1).perPage(5));
assertEquals(size(actions), 5);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects?page=1&per_page=5");
}
public void testListProjectsWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<Project> actions = api.projectApi().list(page(1).perPage(5));
assertTrue(isEmpty(actions));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/projects?page=1&per_page=5");
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.packet.features;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.packet.compute.internal.BasePacketApiLiveTest;
import org.jclouds.packet.domain.SshKey;
import org.jclouds.ssh.SshKeys;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertTrue;
import static org.testng.util.Strings.isNullOrEmpty;
@Test(groups = "live", testName = "SshKeyApiLiveTest")
public class SshKeyApiLiveTest extends BasePacketApiLiveTest {
private SshKey sshKey;
public void testCreate() {
Map<String, String> keyPair = SshKeys.generate();
sshKey = api.sshKeyApi().create(prefix + "-sshkey-livetest", keyPair.get("public"));
}
@Test(dependsOnMethods = "testCreate")
public void testGet() {
api.sshKeyApi().get(sshKey.id());
}
@Test(dependsOnMethods = "testCreate")
public void testList() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(Iterables.all(api().list().concat(), new Predicate<SshKey>() {
@Override
public boolean apply(SshKey input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All ssh keys must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some ssh keys to be returned");
}
@Test(dependsOnMethods = "testCreate")
public void testListOnePage() {
final AtomicInteger found = new AtomicInteger(0);
assertTrue(api().list(page(1).perPage(5)).allMatch(new Predicate<SshKey>() {
@Override
public boolean apply(SshKey input) {
found.incrementAndGet();
return !isNullOrEmpty(input.id());
}
}), "All ssh keys must have the 'id' field populated");
assertTrue(found.get() > 0, "Expected some ssh keys to be returned");
}
@Test(dependsOnMethods = "testList", alwaysRun = true)
public void testDelete() throws InterruptedException {
api.sshKeyApi().delete(sshKey.id());
}
private SshKeyApi api() {
return api.sshKeyApi();
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.packet.features;
import org.jclouds.packet.compute.internal.BasePacketApiMockTest;
import org.jclouds.packet.domain.SshKey;
import org.testng.annotations.Test;
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.size;
import static org.jclouds.packet.domain.options.ListOptions.Builder.page;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@Test(groups = "unit", testName = "SshKeyApiMockTest", singleThreaded = true)
public class SshKeyApiMockTest extends BasePacketApiMockTest {
public void testListSshKeys() throws InterruptedException {
server.enqueue(jsonResponse("/sshKeys-first.json"));
server.enqueue(jsonResponse("/sshKeys-last.json"));
Iterable<SshKey> sshkeys = api.sshKeyApi().list().concat();
assertEquals(size(sshkeys), 8); // Force the PagedIterable to advance
assertEquals(server.getRequestCount(), 2);
assertSent(server, "GET", "/ssh-keys");
assertSent(server, "GET", "/ssh-keys?page=2");
}
public void testListSshKeysReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<SshKey> sshkeys = api.sshKeyApi().list().concat();
assertTrue(isEmpty(sshkeys));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/ssh-keys");
}
public void testListSshKeysWithOptions() throws InterruptedException {
server.enqueue(jsonResponse("/sshKeys-first.json"));
Iterable<SshKey> actions = api.sshKeyApi().list(page(1).perPage(5));
assertEquals(size(actions), 5);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/ssh-keys?page=1&per_page=5");
}
public void testListSshKeysWithOptionsReturns404() throws InterruptedException {
server.enqueue(response404());
Iterable<SshKey> actions = api.sshKeyApi().list(page(1).perPage(5));
assertTrue(isEmpty(actions));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/ssh-keys?page=1&per_page=5");
}
public void testGetSshKey() throws InterruptedException {
server.enqueue(jsonResponse("/ssh-key.json"));
SshKey sshKey = api.sshKeyApi().get("1");
assertEquals(sshKey, objectFromResource("/ssh-key.json", SshKey.class));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/ssh-keys/1");
}
public void testGetSshKeyReturns404() throws InterruptedException {
server.enqueue(response404());
SshKey sshKey = api.sshKeyApi().get("1");
assertNull(sshKey);
assertEquals(server.getRequestCount(), 1);
assertSent(server, "GET", "/ssh-keys/1");
}
public void testCreateSshKey() throws InterruptedException {
server.enqueue(jsonResponse("/ssh-key-create-res.json"));
SshKey sshKey = api.sshKeyApi().create(
"jclouds-ssh-key-livetest",
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdgcoNzH4hCc0j3b4MuG503L/J54uyFvwCAOu8vSsYuLpJ4AEyEOv+T0SfdF605fK6GYXA16Rxk3lrPt7mfKGNtXR0Ripbv7Zc6PvCRorwgj/cjh/45miozjrkXAiHD1GFZycfbi4YsoWAqZj7W4mwtctmhrYM0FPdya2XoRpVy89N+A5Xo4Xtd6EZn6JGEKQM5+kF2aL3ggy0od/DqjuEVYwZoyTe1RgUTXZSU/Woh7WMhsRHbqd3eYz4s6ac8n8IJPGKtUaQeqUtH7OK6NRYXVypUrkqNlwdNYZAwrjXg/x5T3D+bo11LENASRt9OJ2OkmRSTqRxBeDkhnVauWK/"
);
assertEquals(sshKey, objectFromResource("/ssh-key-create-res.json", SshKey.class));
assertEquals(server.getRequestCount(), 1);
assertSent(server, "POST", "/ssh-keys", stringFromResource("/ssh-key-create-req.json"));
}
public void testDeleteSshKey() throws InterruptedException {
server.enqueue(response204());
api.sshKeyApi().delete("1");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "DELETE", "/ssh-keys/1");
}
public void testDeleteSshKeyReturns404() throws InterruptedException {
server.enqueue(response404());
api.sshKeyApi().delete("1");
assertEquals(server.getRequestCount(), 1);
assertSent(server, "DELETE", "/ssh-keys/1");
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.packet.functions;
import org.jclouds.packet.domain.Href;
import org.jclouds.packet.domain.options.ListOptions;
import org.testng.annotations.Test;
import com.google.common.collect.Multimap;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.packet.domain.options.ListOptions.PAGE_PARAM;
import static org.jclouds.packet.domain.options.ListOptions.PER_PAGE_PARAM;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
@Test(groups = "unit", testName = "HrefToListOptionsTest")
public class HrefToListOptionsTest {
public void testNoOptions() {
HrefToListOptions function = new HrefToListOptions();
ListOptions options = function.apply(Href.create("https://api.packet.net/projects"));
assertNotNull(options);
Multimap<String, String> params = options.buildQueryParameters();
assertFalse(params.containsKey(PAGE_PARAM));
assertFalse(params.containsKey(PER_PAGE_PARAM));
}
public void testWithOptions() {
HrefToListOptions function = new HrefToListOptions();
ListOptions options = function.apply(Href.create("https://api.packet.net/projects?page=2&per_page=5"));
assertNotNull(options);
Multimap<String, String> params = options.buildQueryParameters();
assertEquals(getOnlyElement(params.get(PAGE_PARAM)), "2");
assertEquals(getOnlyElement(params.get(PER_PAGE_PARAM)), "5");
}
}

View File

@ -0,0 +1,11 @@
{
"hostname": "jclouds-device-livetest",
"plan": "baremetal_0",
"billing_cycle": "hourly",
"facility": "ewr1",
"features": {},
"operating_system": "ubuntu_16_04",
"locked": false,
"userdata": "",
"tags": []
}

View File

@ -0,0 +1,211 @@
{
"id": "3238ce59-fe02-4b9b-92c1-915ef4f3fb9e",
"short_id": "3238ce59",
"hostname": "andrea-device-livetest",
"description": null,
"state": "queued",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.3238ce59",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-20T14:15:28Z",
"updated_at": "2017-01-20T14:15:29Z",
"provisioning_percentage": 10.0,
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/da5d6c21-2e8c-43ac-820f-ff41bd4e6ebc"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [],
"provisioning_events": [
{
"id": "32a4da09-37dd-446b-99ef-9f64c1eb0097",
"type": "provisioning.101",
"body": "Provisioning started",
"created_at": "2017-01-20T14:15:29Z",
"relationships": [
{
"href": "#5062a5fe-19ea-4b41-864c-e5f4dcb02fa0"
}
],
"interpolated": "Provisioning started",
"href": "/events/32a4da09-37dd-446b-99ef-9f64c1eb0097"
},
{
"id": null,
"type": "provisioning.102",
"body": "Network configured",
"created_at": null,
"relationships": [],
"interpolated": "Network configured"
},
{
"id": null,
"type": "provisioning.103",
"body": "Configuration written, restarting device",
"created_at": null,
"relationships": [],
"interpolated": "Configuration written, restarting device"
},
{
"id": null,
"type": "provisioning.104",
"body": "Connected to magic install system",
"created_at": null,
"relationships": [],
"interpolated": "Connected to magic install system"
},
{
"id": null,
"type": "provisioning.105",
"body": "Server partitions created",
"created_at": null,
"relationships": [],
"interpolated": "Server partitions created"
},
{
"id": null,
"type": "provisioning.106",
"body": "Operating system packages installed",
"created_at": null,
"relationships": [],
"interpolated": "Operating system packages installed"
},
{
"id": null,
"type": "provisioning.107",
"body": "Server networking interfaces configured",
"created_at": null,
"relationships": [],
"interpolated": "Server networking interfaces configured"
},
{
"id": null,
"type": "provisioning.108",
"body": "Cloud-init packages installed and configured",
"created_at": null,
"relationships": [],
"interpolated": "Cloud-init packages installed and configured"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "$n4hwka3!i",
"href": "/devices/3238ce59-fe02-4b9b-92c1-915ef4f3fb9e"
}

View File

@ -0,0 +1,278 @@
{
"id": "98e22032-579e-4c04-bb12-05cc6a3864c8",
"short_id": "98e22032",
"hostname": "test",
"description": null,
"state": "provisioning",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.98e22032",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-03T09:47:59Z",
"updated_at": "2017-01-03T09:50:13Z",
"provisioning_percentage": 50,
"operating_system": {
"id": "68bad60d-f5a7-45c2-ad09-573edaad3a3c",
"slug": "centos_7",
"name": "Centos 7",
"distro": "centos",
"version": "7",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "5d0262c7-1727-411c-94c4-9e6f15490dd3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-03T09:47:59Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "147.75.106.80",
"address": "147.75.106.81",
"gateway": "147.75.106.80",
"href": "/ips/5d0262c7-1727-411c-94c4-9e6f15490dd3"
},
{
"id": "f7d0e65c-eb3b-42bd-af9b-ad3a736d8d43",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-03T09:47:59Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/f7d0e65c-eb3b-42bd-af9b-ad3a736d8d43"
},
{
"id": "a3d00b4e-d74f-4ac2-8bc9-91065d815b41",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-03T09:47:59Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/a3d00b4e-d74f-4ac2-8bc9-91065d815b41"
}
],
"provisioning_events": [
{
"id": "bd62123b-afed-4e54-b1b9-89e219ba9cf0",
"type": "provisioning.101",
"body": "Provisioning started",
"created_at": "2017-01-03T09:47:59Z",
"relationships": [
{
"href": "#81909921-255e-413c-883a-c58d14c801ae"
}
],
"interpolated": "Provisioning started",
"href": "/events/bd62123b-afed-4e54-b1b9-89e219ba9cf0"
},
{
"id": "14b4a9e6-be90-40ee-be48-b272f855e39c",
"type": "provisioning.102",
"body": "Network configured with addresses 147.75.106.81, 2604:1380:2:9800::1, and 10.99.214.1",
"created_at": "2017-01-03T09:48:46Z",
"relationships": [
{
"href": "#28588657-b8bf-44a1-98d3-27f8e04b660a"
}
],
"interpolated": "Network configured with addresses 147.75.106.81, 2604:1380:2:9800::1, and 10.99.214.1",
"href": "/events/14b4a9e6-be90-40ee-be48-b272f855e39c"
},
{
"id": "57e3cadb-f9aa-4c73-be54-3c83e6cf462e",
"type": "provisioning.103",
"body": "Configuration written, restarting device",
"created_at": "2017-01-03T09:48:57Z",
"relationships": [
{
"href": "#25a7e807-f4aa-4f36-83cd-8347baeb26bc"
}
],
"interpolated": "Configuration written, restarting device",
"href": "/events/57e3cadb-f9aa-4c73-be54-3c83e6cf462e"
},
{
"id": "b8322996-f57e-4c87-96cc-a16f33a0c305",
"type": "provisioning.104",
"body": "Connected to magic install system",
"created_at": "2017-01-03T09:50:13Z",
"relationships": [
{
"href": "#a4825d20-f7f2-426a-88db-38696bd3dfd6"
}
],
"interpolated": "Connected to magic install system",
"href": "/events/b8322996-f57e-4c87-96cc-a16f33a0c305"
},
{
"id": "f00a00a9-7c59-420d-bdcf-2c0993303cf6",
"type": "provisioning.105",
"body": "Server partitions created",
"created_at": "2017-01-03T09:50:13Z",
"relationships": [
{
"href": "#6140659b-5e3d-4686-be8f-8879b6d3e27f"
}
],
"interpolated": "Server partitions created",
"href": "/events/f00a00a9-7c59-420d-bdcf-2c0993303cf6"
},
{
"id": null,
"type": "provisioning.106",
"body": "Operating system packages installed",
"created_at": null,
"relationships": [],
"interpolated": "Operating system packages installed"
},
{
"id": null,
"type": "provisioning.107",
"body": "Server networking interfaces configured",
"created_at": null,
"relationships": [],
"interpolated": "Server networking interfaces configured"
},
{
"id": null,
"type": "provisioning.108",
"body": "Cloud-init packages installed and configured",
"created_at": null,
"relationships": [],
"interpolated": "Cloud-init packages installed and configured"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": ",q4*a8(eny",
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
}

View File

@ -0,0 +1,910 @@
{
"devices": [
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f31",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f32",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f33",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f34",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f35",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
}
],
"meta": {
"first": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1"
},
"previous": null,
"self": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1"
},
"next": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=2"
},
"last": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=2"
},
"total": 7
}
}

View File

@ -0,0 +1,376 @@
{
"devices": [
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f36",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
{
"id": "5bd27259-8b71-48f7-879d-c4e695cb2f37",
"short_id": "5bd27259",
"hostname": "andrea-device-livetest",
"description": null,
"state": "active",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.5bd27259",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-18T17:12:29Z",
"updated_at": "2017-01-18T17:16:29Z",
"operating_system": {
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3",
"baremetal_hua"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/f82b69d7-8c7e-4a38-9283-ecdbcaba56aa"
},
{
"href": "/ssh-keys/cf9db551-ec79-4c16-a776-97cdbcb3cec4"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "cbff502b-12ca-4986-b683-2cb4e119ae40",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "147.75.197.54",
"address": "147.75.197.55",
"gateway": "147.75.197.54",
"href": "/ips/cbff502b-12ca-4986-b683-2cb4e119ae40"
},
{
"id": "d5cec621-18c1-4e02-8f71-8dd46370c3e5",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-18T17:12:30Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/d5cec621-18c1-4e02-8f71-8dd46370c3e5"
},
{
"id": "6d206311-1f88-454d-9cf2-afca79f51fa3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-18T17:12:30Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"enabled": true,
"assigned_to": {
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/6d206311-1f88-454d-9cf2-afca79f51fa3"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": "soaz0#a=cd",
"href": "/devices/5bd27259-8b71-48f7-879d-c4e695cb2f31"
}
],
"meta": {
"first": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1"
},
"previous": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=1"
},
"self": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=2"
},
"next": null,
"last": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54/devices?page=2"
},
"total": 7
}
}

View File

@ -0,0 +1,282 @@
{
"devices": [
{
"id": "98e22032-579e-4c04-bb12-05cc6a3864c8",
"short_id": "98e22032",
"hostname": "test",
"description": null,
"state": "provisioning",
"tags": [],
"billing_cycle": "hourly",
"user": "root",
"iqn": "iqn.2017-01.net.packet:device.98e22032",
"locked": false,
"bonding_mode": 5,
"created_at": "2017-01-03T09:47:59Z",
"updated_at": "2017-01-03T09:50:13Z",
"provisioning_percentage": 50,
"operating_system": {
"id": "68bad60d-f5a7-45c2-ad09-573edaad3a3c",
"slug": "centos_7",
"name": "Centos 7",
"distro": "centos",
"version": "7",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
"facility": {
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
"project": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"ssh_keys": [
{
"href": "/ssh-keys/084a5dec-30be-415a-8937-9c615932e459"
},
{
"href": "/ssh-keys/a8d6cc17-7d9d-4fb9-8190-afdb301b67df"
},
{
"href": "/ssh-keys/a3d8bebe-574f-427d-80ee-bc2ba17f7074"
},
{
"href": "/ssh-keys/eacfb002-45e1-4047-a0d5-cd9d8bab19ed"
},
{
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}
],
"project_lite": {
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
"volumes": [],
"ip_addresses": [
{
"id": "5d0262c7-1727-411c-94c4-9e6f15490dd3",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-03T09:47:59Z",
"public": true,
"cidr": 31,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "147.75.106.80",
"address": "147.75.106.81",
"gateway": "147.75.106.80",
"href": "/ips/5d0262c7-1727-411c-94c4-9e6f15490dd3"
},
{
"id": "f7d0e65c-eb3b-42bd-af9b-ad3a736d8d43",
"address_family": 6,
"netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe",
"created_at": "2017-01-03T09:47:59Z",
"public": true,
"cidr": 127,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "2604:1380:2:9800::",
"address": "2604:1380:2:9800::1",
"gateway": "2604:1380:2:9800::",
"href": "/ips/f7d0e65c-eb3b-42bd-af9b-ad3a736d8d43"
},
{
"id": "a3d00b4e-d74f-4ac2-8bc9-91065d815b41",
"address_family": 4,
"netmask": "255.255.255.254",
"created_at": "2017-01-03T09:47:59Z",
"public": false,
"cidr": 31,
"management": true,
"manageable": true,
"assigned_to": {
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
},
"network": "10.99.214.0",
"address": "10.99.214.1",
"gateway": "10.99.214.0",
"href": "/ips/a3d00b4e-d74f-4ac2-8bc9-91065d815b41"
}
],
"provisioning_events": [
{
"id": "bd62123b-afed-4e54-b1b9-89e219ba9cf0",
"type": "provisioning.101",
"body": "Provisioning started",
"created_at": "2017-01-03T09:47:59Z",
"relationships": [
{
"href": "#81909921-255e-413c-883a-c58d14c801ae"
}
],
"interpolated": "Provisioning started",
"href": "/events/bd62123b-afed-4e54-b1b9-89e219ba9cf0"
},
{
"id": "14b4a9e6-be90-40ee-be48-b272f855e39c",
"type": "provisioning.102",
"body": "Network configured with addresses 147.75.106.81, 2604:1380:2:9800::1, and 10.99.214.1",
"created_at": "2017-01-03T09:48:46Z",
"relationships": [
{
"href": "#28588657-b8bf-44a1-98d3-27f8e04b660a"
}
],
"interpolated": "Network configured with addresses 147.75.106.81, 2604:1380:2:9800::1, and 10.99.214.1",
"href": "/events/14b4a9e6-be90-40ee-be48-b272f855e39c"
},
{
"id": "57e3cadb-f9aa-4c73-be54-3c83e6cf462e",
"type": "provisioning.103",
"body": "Configuration written, restarting device",
"created_at": "2017-01-03T09:48:57Z",
"relationships": [
{
"href": "#25a7e807-f4aa-4f36-83cd-8347baeb26bc"
}
],
"interpolated": "Configuration written, restarting device",
"href": "/events/57e3cadb-f9aa-4c73-be54-3c83e6cf462e"
},
{
"id": "b8322996-f57e-4c87-96cc-a16f33a0c305",
"type": "provisioning.104",
"body": "Connected to magic install system",
"created_at": "2017-01-03T09:50:13Z",
"relationships": [
{
"href": "#a4825d20-f7f2-426a-88db-38696bd3dfd6"
}
],
"interpolated": "Connected to magic install system",
"href": "/events/b8322996-f57e-4c87-96cc-a16f33a0c305"
},
{
"id": "f00a00a9-7c59-420d-bdcf-2c0993303cf6",
"type": "provisioning.105",
"body": "Server partitions created",
"created_at": "2017-01-03T09:50:13Z",
"relationships": [
{
"href": "#6140659b-5e3d-4686-be8f-8879b6d3e27f"
}
],
"interpolated": "Server partitions created",
"href": "/events/f00a00a9-7c59-420d-bdcf-2c0993303cf6"
},
{
"id": null,
"type": "provisioning.106",
"body": "Operating system packages installed",
"created_at": null,
"relationships": [],
"interpolated": "Operating system packages installed"
},
{
"id": null,
"type": "provisioning.107",
"body": "Server networking interfaces configured",
"created_at": null,
"relationships": [],
"interpolated": "Server networking interfaces configured"
},
{
"id": null,
"type": "provisioning.108",
"body": "Cloud-init packages installed and configured",
"created_at": null,
"relationships": [],
"interpolated": "Cloud-init packages installed and configured"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
},
{
"id": null,
"type": "provisioning.109",
"body": "Installation finished, rebooting server",
"created_at": null,
"relationships": [],
"interpolated": "Installation finished, rebooting server"
}
],
"plan": {
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
"userdata": "",
"root_password": ",q4*a8(eny",
"href": "/devices/98e22032-579e-4c04-bb12-05cc6a3864c8"
}
]
}

View File

@ -0,0 +1,39 @@
{
"facilities": [
{
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
{
"id": "8e6470b3-b75e-47d1-bb93-45b225750975",
"name": "Amsterdam, NL",
"code": "ams1",
"features": [
"storage"
],
"address": null
}
],
"meta": {
"first": {
"href": "/facilities?page=1"
},
"previous": null,
"self": {
"href": "/facilities?page=1"
},
"next": {
"href": "/facilities?page=2"
},
"last": {
"href": "/facilities?page=2"
},
"total": 3
}
}

View File

@ -0,0 +1,27 @@
{
"facilities": [
{
"id": "2b70eb8f-fa18-47c0-aba7-222a842362fd",
"name": "Sunnyvale, CA",
"code": "sjc1",
"features": [],
"address": null
}
],
"meta": {
"first": {
"href": "/facilities?page=1"
},
"previous": {
"href": "/facilities?page=1"
},
"self": {
"href": "/facilities?page=2"
},
"next": null,
"last": {
"href": "/facilities?page=2"
},
"total": 3
}
}

View File

@ -0,0 +1,30 @@
{
"facilities": [
{
"id": "e1e9c52e-a0bc-4117-b996-0fc94843ea09",
"name": "Parsippany, NJ",
"code": "ewr1",
"features": [
"baremetal",
"storage"
],
"address": null
},
{
"id": "8e6470b3-b75e-47d1-bb93-45b225750975",
"name": "Amsterdam, NL",
"code": "ams1",
"features": [
"storage"
],
"address": null
},
{
"id": "2b70eb8f-fa18-47c0-aba7-222a842362fd",
"name": "Sunnyvale, CA",
"code": "sjc1",
"features": [],
"address": null
}
]
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<configuration scan="false">
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
<file>target/test-data/jclouds-wire.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
<file>target/jclouds-compute.log</file>
<encoder>
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
</encoder>
</appender>
<logger name="org.jclouds">
<level value="DEBUG" />
<appender-ref ref="FILE" />
</logger>
<logger name="jclouds.compute">
<level value="DEBUG" />
<appender-ref ref="COMPUTEFILE" />
</logger>
<logger name="jclouds.wire">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
<logger name="jclouds.headers">
<level value="DEBUG" />
<appender-ref ref="WIREFILE" />
</logger>
<root>
<level value="INFO" />
</root>
</configuration>

View File

@ -0,0 +1,96 @@
{
"operating_systems": [
{
"id": "06e21644-a769-11e6-80f5-76304dec7eb7",
"slug": "alpine_3",
"name": "Alpine 3",
"distro": "alpine",
"version": "3",
"provisionable_on": []
},
{
"id": "06e21978-a769-11e6-80f5-76304dec7eb7",
"slug": "centos_6",
"name": "CentOS 6",
"distro": "centos",
"version": "6",
"provisionable_on": []
},
{
"id": "68bad60d-f5a7-45c2-ad09-573edaad3a3c",
"slug": "centos_7",
"name": "Centos 7",
"distro": "centos",
"version": "7",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21ce8-a769-11e6-80f5-76304dec7eb7",
"slug": "coreos_alpha",
"name": "CoreOS Alpha",
"distro": "coreos",
"version": "alpha",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "6345f310-0bb0-4b59-b051-954fed05183a",
"slug": "coreos_beta",
"name": "CoreOS Beta",
"distro": "coreos",
"version": "beta",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "d61c3912-8422-4daf-835e-854efa0062e4",
"slug": "coreos_stable",
"name": "CoreOS Stable",
"distro": "coreos",
"version": "stable",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21e78-a769-11e6-80f5-76304dec7eb7",
"slug": "debian_7",
"name": "Debian 7",
"distro": "debian",
"version": "7",
"provisionable_on": []
}
],
"meta": {
"first": {
"href": "/operating-systems?page=1"
},
"previous": null,
"self": {
"href": "/operating-systems?page=1"
},
"next": {
"href": "/operating-systems?page=2"
},
"last": {
"href": "/operating-systems?page=2"
},
"total": 14
}
}

View File

@ -0,0 +1,106 @@
{
"operating_systems": [
{
"id": "88239019-abc7-41e8-9a4d-cd334da97ff1",
"slug": "debian_8",
"name": "Debian 8",
"distro": "debian",
"version": "8",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21ffe-a769-11e6-80f5-76304dec7eb7",
"slug": "deprovision",
"name": "Deprovision",
"distro": "centos",
"version": "",
"provisionable_on": []
},
{
"id": "06e22198-a769-11e6-80f5-76304dec7eb7",
"slug": "freebsd_10_3",
"name": "FreeBSD 10.3",
"distro": "freebsd",
"version": "10.3",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e22328-a769-11e6-80f5-76304dec7eb7",
"slug": "rancher",
"name": "RancherOS",
"distro": "rancher",
"version": "latest",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e224b8-a769-11e6-80f5-76304dec7eb7",
"slug": "ubuntu_14_04",
"name": "Ubuntu 14.04 LTS",
"distro": "ubuntu",
"version": "14.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3"
]
},
{
"id": "06e22972-a769-11e6-80f5-76304dec7eb7",
"slug": "windows_2012_rc2",
"name": "Windows 2012 RC2",
"distro": "windows",
"version": "2012 RC2",
"provisionable_on": [
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
}
],
"meta": {
"first": {
"href": "/operating-systems?page=1"
},
"previous": {
"href": "/operating-systems?page=1"
},
"self": {
"href": "/operating-systems?page=2"
},
"next": null,
"last": {
"href": "/operating-systems?page=2"
},
"total": 14
}
}

View File

@ -0,0 +1,166 @@
{
"operating_systems": [
{
"id": "06e21644-a769-11e6-80f5-76304dec7eb7",
"slug": "alpine_3",
"name": "Alpine 3",
"distro": "alpine",
"version": "3",
"provisionable_on": []
},
{
"id": "06e21978-a769-11e6-80f5-76304dec7eb7",
"slug": "centos_6",
"name": "CentOS 6",
"distro": "centos",
"version": "6",
"provisionable_on": []
},
{
"id": "68bad60d-f5a7-45c2-ad09-573edaad3a3c",
"slug": "centos_7",
"name": "Centos 7",
"distro": "centos",
"version": "7",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21ce8-a769-11e6-80f5-76304dec7eb7",
"slug": "coreos_alpha",
"name": "CoreOS Alpha",
"distro": "coreos",
"version": "alpha",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "6345f310-0bb0-4b59-b051-954fed05183a",
"slug": "coreos_beta",
"name": "CoreOS Beta",
"distro": "coreos",
"version": "beta",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "d61c3912-8422-4daf-835e-854efa0062e4",
"slug": "coreos_stable",
"name": "CoreOS Stable",
"distro": "coreos",
"version": "stable",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21e78-a769-11e6-80f5-76304dec7eb7",
"slug": "debian_7",
"name": "Debian 7",
"distro": "debian",
"version": "7",
"provisionable_on": []
},
{
"id": "88239019-abc7-41e8-9a4d-cd334da97ff1",
"slug": "debian_8",
"name": "Debian 8",
"distro": "debian",
"version": "8",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e21ffe-a769-11e6-80f5-76304dec7eb7",
"slug": "deprovision",
"name": "Deprovision",
"distro": "centos",
"version": "",
"provisionable_on": []
},
{
"id": "06e22198-a769-11e6-80f5-76304dec7eb7",
"slug": "freebsd_10_3",
"name": "FreeBSD 10.3",
"distro": "freebsd",
"version": "10.3",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e22328-a769-11e6-80f5-76304dec7eb7",
"slug": "rancher",
"name": "RancherOS",
"distro": "rancher",
"version": "latest",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "06e224b8-a769-11e6-80f5-76304dec7eb7",
"slug": "ubuntu_14_04",
"name": "Ubuntu 14.04 LTS",
"distro": "ubuntu",
"version": "14.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
},
{
"id": "1b9b78e3-de68-466e-ba00-f2123e89c112",
"slug": "ubuntu_16_04",
"name": "Ubuntu 16.04 LTS",
"distro": "ubuntu",
"version": "16.04",
"provisionable_on": [
"baremetal_0",
"baremetal_1",
"baremetal_2",
"baremetal_2a",
"baremetal_3"
]
},
{
"id": "06e22972-a769-11e6-80f5-76304dec7eb7",
"slug": "windows_2012_rc2",
"name": "Windows 2012 RC2",
"distro": "windows",
"version": "2012 RC2",
"provisionable_on": [
"baremetal_1",
"baremetal_2",
"baremetal_3"
]
}
]
}

View File

@ -0,0 +1,222 @@
{
"plans": [
{
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
{
"id": "6d1f1ffa-7912-4b78-b50d-88cc7c8ab40f",
"slug": "baremetal_1",
"name": "Type 1",
"description": "Our Type 1 configuration is a zippy general use server, with an Intel E3-1240 v3 processor and 32GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel E3-1240 v3"
}
],
"memory": {
"total": "32GB"
},
"drives": [
{
"count": 2,
"size": "120GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.4
}
},
{
"id": "a3729923-fdc4-4e85-a972-aafbad3695db",
"slug": "baremetal_2",
"name": "Type 2",
"description": "Our Type 2 configuration is the perfect all purpose virtualization server, with dual E5-2650 v4 processors, 256 GB of DDR4 RAM, and six SSDs totaling 2.8 TB of storage.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Intel Xeon E5-2650 v4 @2.2GHz"
}
],
"memory": {
"total": "256GB"
},
"drives": [
{
"count": 6,
"size": "480GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 1.25
}
},
{
"id": "3bc8a214-b807-4058-ad4a-6925f2411155",
"slug": "baremetal_2a",
"name": "Type 2A",
"description": "Our Type 2A configuration is a 96-core dual socket ARM 64 beast based on Cavium ThunderX chips",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Cavium ThunderX CN8890 @2GHz"
}
],
"memory": {
"total": "128GB"
},
"drives": [
{
"count": 1,
"size": "512GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.5
}
}
],
"meta": {
"first": {
"href": "/plans?page=1"
},
"previous": null,
"self": {
"href": "/plans?page=1"
},
"next": {
"href": "/plans?page=2"
},
"last": {
"href": "/plans?page=2"
},
"total": 7
}
}

View File

@ -0,0 +1,98 @@
{
"plans": [
{
"id": "741f3afb-bb2f-4694-93a0-fcbad7cd5e78",
"slug": "baremetal_3",
"name": "Type 3",
"description": "Our Type 3 configuration is a high core, high IO server, with dual Intel E5-2640 v3 processors, 128GB of DDR4 RAM and ultra fast NVME flash drives.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Intel E5-2640 v3"
}
],
"memory": {
"total": "128GB"
},
"drives": [
{
"count": 2,
"size": "120GB",
"type": "SSD"
},
{
"count": 1,
"size": "1.6TB",
"type": "NVME"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 1.75
}
},
{
"id": "87728148-3155-4992-a730-8d1e6aca8a32",
"slug": "storage_1",
"name": "Standard",
"description": "TBD",
"line": "storage",
"specs": {},
"available_in": [],
"pricing": {
"hour": 0.000104
}
},
{
"id": "d6570cfb-38fa-4467-92b3-e45d059bb249",
"slug": "storage_2",
"name": "Performance",
"description": "TBD",
"line": "storage",
"specs": {},
"available_in": [],
"pricing": {
"hour": 0.000223
}
}
],
"meta": {
"first": {
"href": "/plans?page=1"
},
"previous": {
"href": "/plans?page=1"
},
"self": {
"href": "/plans?page=2"
},
"next": null,
"last": {
"href": "/plans?page=2"
},
"total": 7
}
}

View File

@ -0,0 +1,284 @@
{
"plans": [
{
"id": "e69c0169-4726-46ea-98f1-939c9e8a3607",
"slug": "baremetal_0",
"name": "Type 0",
"description": "Our Type 0 configuration is a general use \"cloud killer\" server, with a Intel Atom 2.4Ghz processor and 8GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel Atom C2550 @ 2.4Ghz"
}
],
"memory": {
"total": "8GB"
},
"drives": [
{
"count": 1,
"size": "80GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": false,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.05
}
},
{
"id": "6d1f1ffa-7912-4b78-b50d-88cc7c8ab40f",
"slug": "baremetal_1",
"name": "Type 1",
"description": "Our Type 1 configuration is a zippy general use server, with an Intel E3-1240 v3 processor and 32GB of RAM.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 1,
"type": "Intel E3-1240 v3"
}
],
"memory": {
"total": "32GB"
},
"drives": [
{
"count": 2,
"size": "120GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "1Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.4
}
},
{
"id": "a3729923-fdc4-4e85-a972-aafbad3695db",
"slug": "baremetal_2",
"name": "Type 2",
"description": "Our Type 2 configuration is the perfect all purpose virtualization server, with dual E5-2650 v4 processors, 256 GB of DDR4 RAM, and six SSDs totaling 2.8 TB of storage.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Intel Xeon E5-2650 v4 @2.2GHz"
}
],
"memory": {
"total": "256GB"
},
"drives": [
{
"count": 6,
"size": "480GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 1.25
}
},
{
"id": "3bc8a214-b807-4058-ad4a-6925f2411155",
"slug": "baremetal_2a",
"name": "Type 2A",
"description": "Our Type 2A configuration is a 96-core dual socket ARM 64 beast based on Cavium ThunderX chips",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Cavium ThunderX CN8890 @2GHz"
}
],
"memory": {
"total": "128GB"
},
"drives": [
{
"count": 1,
"size": "512GB",
"type": "SSD"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/8ea03255-89f9-4e62-9d3f-8817db82ceed"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 0.5
}
},
{
"id": "741f3afb-bb2f-4694-93a0-fcbad7cd5e78",
"slug": "baremetal_3",
"name": "Type 3",
"description": "Our Type 3 configuration is a high core, high IO server, with dual Intel E5-2640 v3 processors, 128GB of DDR4 RAM and ultra fast NVME flash drives.",
"line": "baremetal",
"specs": {
"cpus": [
{
"count": 2,
"type": "Intel E5-2640 v3"
}
],
"memory": {
"total": "128GB"
},
"drives": [
{
"count": 2,
"size": "120GB",
"type": "SSD"
},
{
"count": 1,
"size": "1.6TB",
"type": "NVME"
}
],
"nics": [
{
"count": 2,
"type": "10Gbps"
}
],
"features": {
"raid": true,
"txt": true
}
},
"available_in": [
{
"href": "/facilities/2b70eb8f-fa18-47c0-aba7-222a842362fd"
},
{
"href": "/facilities/8e6470b3-b75e-47d1-bb93-45b225750975"
},
{
"href": "/facilities/e1e9c52e-a0bc-4117-b996-0fc94843ea09"
}
],
"pricing": {
"hour": 1.75
}
},
{
"id": "87728148-3155-4992-a730-8d1e6aca8a32",
"slug": "storage_1",
"name": "Standard",
"description": "TBD",
"line": "storage",
"specs": {},
"available_in": [],
"pricing": {
"hour": 0.000104
}
},
{
"id": "d6570cfb-38fa-4467-92b3-e45d059bb249",
"slug": "storage_2",
"name": "Performance",
"description": "TBD",
"line": "storage",
"specs": {},
"available_in": [],
"pricing": {
"hour": 0.000223
}
}
]
}

View File

@ -0,0 +1,3 @@
{
"type": "power_off"
}

View File

@ -0,0 +1,3 @@
{
"type": "power_on"
}

View File

@ -0,0 +1,315 @@
{
"projects": [
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a54",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a53",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a52",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a51",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a50",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
}
],
"meta": {
"first": {
"href": "/projects?page=1"
},
"previous": null,
"self": {
"href": "/projects?page=1"
},
"next": {
"href": "/projects?page=2"
},
"last": {
"href": "/projects?page=2"
},
"total": 8
}
}

View File

@ -0,0 +1,197 @@
{
"projects": [
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a55",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a56",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
},
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a57",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
}
],
"meta": {
"first": {
"href": "/projects?page=1"
},
"previous": {
"href": "/projects?page=1"
},
"self": {
"href": "/projects?page=2"
},
"next": null,
"last": {
"href": "/projects?page=2"
},
"total": 8
}
}

View File

@ -0,0 +1,77 @@
{
"projects": [
{
"id": "93907f48-adfe-43ed-ad89-0e6e83721a54",
"name": "Cloudsoft CCS Testing",
"created_at": "2016-09-15T08:50:58Z",
"updated_at": "2017-01-05T09:36:53Z",
"max_devices": {
"baremetal_0": null,
"baremetal_1": null,
"baremetal_2": null,
"baremetal_3": null,
"baremetal_2a": null,
"storage_1": null,
"storage_2": null
},
"members": [
{
"href": "/users/1140617d-262d-4502-a3d6-771d83c930da"
},
{
"href": "/users/343345fe-18b3-46a3-9b9c-e4a2fe88ccbd"
},
{
"href": "/users/73b0442e-cc4b-42a0-8d3a-c8dfb8a4ff2e"
},
{
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
}
],
"memberships": [
{
"href": "/memberships/914facae-547f-46fc-93e8-860eb53d9bf6"
},
{
"href": "/memberships/1ce7c9d9-d11f-47f2-b5a6-d1221338ad69"
},
{
"href": "/memberships/e25c4478-9e44-465a-a9d7-bffc7a83300d"
},
{
"href": "/memberships/8a00c05b-3ddc-41c3-8dd7-4a3d5984ecec"
}
],
"invitations": [],
"payment_method": {
"href": "/payment-methods/b2bb4ee0-506b-4c01-b17d-ba41d5c430c5"
},
"devices": [
{
"href": "/devices/4942a6c7-55c6-42cc-a06b-cccd6f2fa5df"
}
],
"ssh_keys": [
{
"href": "/ssh-keys/070e3282-5b6a-4f75-8f18-a4e7488eafaa"
}
],
"volumes": [],
"href": "/projects/93907f48-adfe-43ed-ad89-0e6e83721a54"
}
],
"meta": {
"first": {
"href": "/projects?page=1"
},
"previous": null,
"self": {
"href": "/projects?page=1"
},
"next": null,
"last": {
"href": "/projects?page=1"
},
"total": 1
}
}

View File

@ -0,0 +1,3 @@
{
"type": "reboot"
}

View File

@ -0,0 +1,4 @@
{
"label": "jclouds-ssh-key-livetest",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdgcoNzH4hCc0j3b4MuG503L/J54uyFvwCAOu8vSsYuLpJ4AEyEOv+T0SfdF605fK6GYXA16Rxk3lrPt7mfKGNtXR0Ripbv7Zc6PvCRorwgj/cjh/45miozjrkXAiHD1GFZycfbi4YsoWAqZj7W4mwtctmhrYM0FPdya2XoRpVy89N+A5Xo4Xtd6EZn6JGEKQM5+kF2aL3ggy0od/DqjuEVYwZoyTe1RgUTXZSU/Woh7WMhsRHbqd3eYz4s6ac8n8IJPGKtUaQeqUtH7OK6NRYXVypUrkqNlwdNYZAwrjXg/x5T3D+bo11LENASRt9OJ2OkmRSTqRxBeDkhnVauWK/"
}

View File

@ -0,0 +1,12 @@
{
"id": "da5d6c21-2e8c-43ac-820f-ff41bd4e6ebc",
"label": "andrea-device-livetest",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdgcoNzH4hCc0j3b4MuG503L/J54uyFvwCAOu8vSsYuLpJ4AEyEOv+T0SfdF605fK6GYXA16Rxk3lrPt7mfKGNtXR0Ripbv7Zc6PvCRorwgj/cjh/45miozjrkXAiHD1GFZycfbi4YsoWAqZj7W4mwtctmhrYM0FPdya2XoRpVy89N+A5Xo4Xtd6EZn6JGEKQM5+kF2aL3ggy0od/DqjuEVYwZoyTe1RgUTXZSU/Woh7WMhsRHbqd3eYz4s6ac8n8IJPGKtUaQeqUtH7OK6NRYXVypUrkqNlwdNYZAwrjXg/x5T3D+bo11LENASRt9OJ2OkmRSTqRxBeDkhnVauWK/",
"fingerprint": "60:b0:5a:23:42:b8:53:19:d0:32:f1:4d:a8:7c:9b:71",
"created_at": "2017-01-20T14:15:26Z",
"updated_at": "2017-01-20T14:15:26Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/da5d6c21-2e8c-43ac-820f-ff41bd4e6ebc"
}

View File

@ -0,0 +1,12 @@
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247ed",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}

View File

@ -0,0 +1,80 @@
{
"ssh_keys": [
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e1",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e2",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e3",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e4",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e5",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}
],
"meta": {
"first": {
"href": "/ssh-keys?page=1"
},
"previous": null,
"self": {
"href": "/ssh-keys?page=1"
},
"next": {
"href": "/ssh-keys?page=2"
},
"last": {
"href": "/ssh-keys?page=2"
},
"total": 8
}
}

View File

@ -0,0 +1,56 @@
{
"ssh_keys": [
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e6",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e7",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
},
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247e8",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}
],
"meta": {
"first": {
"href": "/ssh-keys?page=1"
},
"previous": {
"href": "/ssh-keys?page=1"
},
"self": {
"href": "/ssh-keys?page=2"
},
"next": null,
"last": {
"href": "/ssh-keys?page=2"
},
"total": 8
}
}

View File

@ -0,0 +1,16 @@
{
"ssh_keys": [
{
"id": "bba63e41-b12c-493a-81d4-e52f50f247ed",
"label": "test",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNnvPZ25wZK19grrbal6R5JP1hLRBUuNh86KxZVMAFkGd5ouVSgO9dFajHZ45Q4mbaTkdOiqf7otMLDzkaztEa7oLK7Jso0Y0LOi+nT4gf38rvbEF5mq069G9b9XqlNleaGnpLuTN54iEK8c4TWZxIJqgelHEHhjp7V1asmilBbpZDmwA5cTt9vGJIhqA/BptKH3folZKeAQjRa1ZRSwSQUnk9rBKn4PKSRDojVa9A9jKt4qboJh5q7ZjtE8z+665F/4TgLzElTXUA8+uUFGpuynMSmQEt301e18dXAl+vBr8fMiThcoVVbdVdqdjXsw75fMXPAgqhjrw8k3+0/4P9 andrea@bigmac.local",
"fingerprint": "88:8a:7d:0f:db:fb:f4:2a:63:2e:d3:4b:1f:7f:0b:12",
"created_at": "2017-01-03T09:42:54Z",
"updated_at": "2017-01-03T09:42:54Z",
"owner": {
"href": "/users/ad711bc3-6333-449a-a405-23ca81f38c00"
},
"href": "/ssh-keys/bba63e41-b12c-493a-81d4-e52f50f247ed"
}
]
}

View File

@ -0,0 +1,19 @@
{
"id": "faaaec30-be0c-4b6d-b1a7-1bf6e494a333",
"first_name": "Melissa",
"last_name": "Smith",
"full_name": "Melissa Smith",
"email": "melissa@example.com",
"max_projects": 1,
"two_factor_auth": "sms",
"timezone": "America/New_York",
"phone_number": "555-555-5555",
"created_at": "2014-04-14T02:15:15Z",
"updated_at": "2014-04-14T02:15:15Z",
"timezone": "America/New_York",
"emails": [
{ "href": "/emails/7e407a6a-4f96-4da0-8c48-5665352ebf06" }
],
"avatar_url": "https://www.gravatar.com/avatar/d248bbb3a03ed?d=https://app.packet.net/client-portal/assets/images/default_gravatar.png",
"href": "/users/faaaec30-be0c-4b6d-b1a7-1bf6e494a333"
}