blob: 5e27c4ba3ce77f89b7e27ecfecea58b65690d514 [file] [log] [blame]
/*
* 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.apache.jclouds.oneandone.rest.compute;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.base.Predicate;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.common.base.Throwables;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.apache.jclouds.oneandone.rest.OneAndOneApi;
import org.apache.jclouds.oneandone.rest.compute.strategy.CleanupResources;
import static org.apache.jclouds.oneandone.rest.config.OneAndOneProperties.POLL_PREDICATE_SERVER;
import org.apache.jclouds.oneandone.rest.domain.DataCenter;
import org.apache.jclouds.oneandone.rest.domain.FirewallPolicy;
import org.apache.jclouds.oneandone.rest.domain.HardwareFlavour;
import org.apache.jclouds.oneandone.rest.domain.Hdd;
import org.apache.jclouds.oneandone.rest.domain.Server;
import org.apache.jclouds.oneandone.rest.domain.ServerAppliance;
import org.apache.jclouds.oneandone.rest.domain.SingleServerAppliance;
import org.apache.jclouds.oneandone.rest.domain.Types;
import org.apache.jclouds.oneandone.rest.domain.options.GenericQueryOptions;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Hardware;
import org.jclouds.compute.domain.Template;
import org.jclouds.compute.domain.Volume;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.compute.util.ComputeServiceUtils;
import static org.jclouds.compute.util.ComputeServiceUtils.getPortRangesFromList;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.logging.Logger;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.PasswordGenerator;
@Singleton
public class OneandoneComputeServiceAdapter implements ComputeServiceAdapter<Server, HardwareFlavour, SingleServerAppliance, DataCenter> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final CleanupResources cleanupResources;
private final OneAndOneApi api;
private final Predicate<Server> waitServerUntilAvailable;
private final PasswordGenerator.Config passwordGenerator;
@Inject
OneandoneComputeServiceAdapter(OneAndOneApi api, CleanupResources cleanupResources,
@Named(POLL_PREDICATE_SERVER) Predicate<Server> waitServerUntilAvailable,
PasswordGenerator.Config passwordGenerator) {
this.api = api;
this.cleanupResources = cleanupResources;
this.waitServerUntilAvailable = waitServerUntilAvailable;
this.passwordGenerator = passwordGenerator;
}
@Override
public NodeAndInitialCredentials<Server> createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
final String dataCenterId = template.getLocation().getId();
Hardware hardware = template.getHardware();
TemplateOptions options = template.getOptions();
Server updateServer = null;
final String loginUser = isNullOrEmpty(options.getLoginUser()) ? "root" : options.getLoginUser();
final String password = options.hasLoginPassword() ? options.getLoginPassword() : passwordGenerator.generate();
final String privateKey = options.hasLoginPrivateKey() ? options.getPrivateKey() : null;
final org.jclouds.compute.domain.Image image = template.getImage();
final int[] inboundPorts = template.getOptions().getInboundPorts();
//prepare hdds to provision
List<? extends Volume> volumes = hardware.getVolumes();
List<Hdd.CreateHdd> hdds = new ArrayList<Hdd.CreateHdd>();
for (final Volume volume : volumes) {
try {
//check if the bootable device has enough size to run the appliance(image).
float minHddSize = volume.getSize();
if (volume.isBootDevice()) {
SingleServerAppliance appliance = api.serverApplianceApi().get(image.getId());
if (appliance.minHddSize() > volume.getSize()) {
minHddSize = appliance.minHddSize();
}
}
Hdd.CreateHdd hdd = Hdd.CreateHdd.create(minHddSize, volume.isBootDevice());
hdds.add(hdd);
} catch (Exception ex) {
throw Throwables.propagate(ex);
}
}
// provision server
Server server = null;
Double cores = ComputeServiceUtils.getCores(hardware);
Double ram = (double) hardware.getRam();
if (ram < 1024) {
ram = 0.5;
} else {
ram = ram / 1024;
}
try {
org.apache.jclouds.oneandone.rest.domain.Hardware.CreateHardware hardwareRequest
= org.apache.jclouds.oneandone.rest.domain.Hardware.CreateHardware.create(cores, 1, ram, hdds);
final Server.CreateServer serverRequest = Server.CreateServer.builder()
.name(name)
.description(name)
.hardware(hardwareRequest)
.rsaKey(options.getPublicKey())
.password(privateKey == null ? password : null)
.applianceId(image.getId())
.dataCenterId(dataCenterId)
.powerOn(Boolean.TRUE).build();
logger.trace("<< provisioning server '%s'", serverRequest);
server = api.serverApi().create(serverRequest);
waitServerUntilAvailable.apply(server);
updateServer = api.serverApi().get(server.id());
Map<Integer, Integer> portsRange = getPortRangesFromList(inboundPorts);
List<FirewallPolicy.Rule.CreatePayload> rules = new ArrayList<FirewallPolicy.Rule.CreatePayload>();
for (Map.Entry<Integer, Integer> range : portsRange.entrySet()) {
FirewallPolicy.Rule.CreatePayload rule = FirewallPolicy.Rule.CreatePayload.builder()
.portFrom(range.getKey())
.portTo(range.getValue())
.protocol(Types.RuleProtocol.TCP)
.build();
rules.add(rule);
}
if (inboundPorts.length > 0) {
FirewallPolicy rule = api.firewallPolicyApi().create(FirewallPolicy.CreateFirewallPolicy.create(server.name() + " firewall policy", "desc", rules));
api.serverApi().addFirewallPolicy(updateServer.id(), updateServer.ips().get(0).id(), rule.id());
waitServerUntilAvailable.apply(server);
}
logger.trace(">> provisioning complete for server. returned id='%s'", server.id());
} catch (Exception ex) {
logger.error(ex, ">> failed to provision server. rollbacking..");
if (server != null) {
destroyNode(server.id());
}
throw Throwables.propagate(ex);
}
LoginCredentials serverCredentials = LoginCredentials.builder()
.user(loginUser)
.password(password)
.privateKey(privateKey)
.build();
return new NodeAndInitialCredentials<Server>(updateServer, updateServer.id(), serverCredentials);
}
@Override
public List<HardwareFlavour> listHardwareProfiles() {
return api.serverApi().listHardwareFlavours();
}
@Override
public Iterable<SingleServerAppliance> listImages() {
GenericQueryOptions options = new GenericQueryOptions();
options.options(0, 0, null, null, null);
List<ServerAppliance> list = api.serverApplianceApi().list(options);
List<SingleServerAppliance> results = new ArrayList<SingleServerAppliance>();
for (ServerAppliance appliance : list) {
List<SingleServerAppliance.AvailableDataCenters> availableDatacenters = new ArrayList<SingleServerAppliance.AvailableDataCenters>();
for (String dcId : appliance.availableDataCenters()) {
availableDatacenters.add(SingleServerAppliance.AvailableDataCenters.create(dcId, ""));
}
results.add(SingleServerAppliance.builder()
.id(appliance.id())
.name(appliance.name())
.availableDataCenters(availableDatacenters)
.osInstallationBase(appliance.osInstallationBase())
.osFamily(appliance.osFamily())
.os(appliance.os())
.osVersion(appliance.osVersion())
.osArchitecture(appliance.osArchitecture())
.osImageType(appliance.osImageType())
.minHddSize(appliance.minHddSize())
.type(appliance.type())
.state(appliance.state())
.version(appliance.version())
.categories(appliance.categories())
.eulaUrl(appliance.eulaUrl())
.build());
}
return results;
}
@Override
public SingleServerAppliance getImage(String id) {
// try search images
logger.trace("<< searching for image with id=%s", id);
GenericQueryOptions options = new GenericQueryOptions();
options.options(0, 0, null, id, null);
try {
List<ServerAppliance> list = api.serverApplianceApi().list(options);
if (list.size() > 0) {
ServerAppliance appliance = list.get(0);
List<SingleServerAppliance.AvailableDataCenters> availableDatacenters = new ArrayList<SingleServerAppliance.AvailableDataCenters>();
for (String dcId : appliance.availableDataCenters()) {
availableDatacenters.add(SingleServerAppliance.AvailableDataCenters.create(dcId, ""));
}
SingleServerAppliance image = SingleServerAppliance.create(appliance.id(), appliance.name(), availableDatacenters, appliance.osInstallationBase(),
appliance.osFamily(), appliance.os(), appliance.osVersion(), appliance.osArchitecture(), appliance.osImageType(), appliance.minHddSize(),
appliance.type(), appliance.state(), appliance.version(), appliance.categories(), appliance.eulaUrl());
logger.trace(">> found image [%s].", image.name());
return image;
}
} catch (Exception ex) {
throw new ResourceNotFoundException("No image with id '" + id + "' was found");
}
throw new ResourceNotFoundException("No image with id '" + id + "' was found");
}
@Override
public Iterable<DataCenter> listLocations() {
return api.dataCenterApi().list();
}
@Override
public Server getNode(String id) {
return api.serverApi().get(id);
}
@Override
public void destroyNode(String id) {
checkState(cleanupResources.cleanupNode(id), "server(%s) and its resources still there after deleting!?", id);
}
@Override
public void rebootNode(String id) {
waitServerUntilAvailable.apply(getNode(id));
api.serverApi().updateStatus(id, Server.UpdateStatus.create(Types.ServerAction.REBOOT, Types.ServerActionMethod.HARDWARE));
}
@Override
public void resumeNode(String id) {
api.serverApi().updateStatus(id, Server.UpdateStatus.create(Types.ServerAction.POWER_ON, Types.ServerActionMethod.HARDWARE));
}
@Override
public void suspendNode(String id) {
waitServerUntilAvailable.apply(getNode(id));
api.serverApi().updateStatus(id, Server.UpdateStatus.create(Types.ServerAction.POWER_OFF, Types.ServerActionMethod.HARDWARE));
}
@Override
public Iterable<Server> listNodes() {
return api.serverApi().list();
}
@Override
public Iterable<Server> listNodesByIds(final Iterable<String> ids) {
return filter(listNodes(), new Predicate<Server>() {
@Override
public boolean apply(Server server) {
return contains(ids, server);
}
});
}
}