Package io.fathom.cloud.compute.api.os.resources

Source Code of io.fathom.cloud.compute.api.os.resources.ServersResource

package io.fathom.cloud.compute.api.os.resources;

import io.fathom.cloud.CloudException;
import io.fathom.cloud.compute.actions.StartInstancesAction;
import io.fathom.cloud.compute.actions.StopInstancesAction;
import io.fathom.cloud.compute.api.os.model.Address;
import io.fathom.cloud.compute.api.os.model.Addresses;
import io.fathom.cloud.compute.api.os.model.Flavor;
import io.fathom.cloud.compute.api.os.model.Image;
import io.fathom.cloud.compute.api.os.model.SecurityGroup;
import io.fathom.cloud.compute.api.os.model.SecurityGroupList;
import io.fathom.cloud.compute.api.os.model.Server;
import io.fathom.cloud.compute.api.os.model.ServerList;
import io.fathom.cloud.compute.api.os.model.VolumeAttachments;
import io.fathom.cloud.compute.api.os.model.WrappedServer;
import io.fathom.cloud.compute.api.os.model.actions.AddFloatingIpRequest;
import io.fathom.cloud.compute.api.os.model.actions.AddSecurityGroupRequest;
import io.fathom.cloud.compute.api.os.model.actions.CreateImageRequest;
import io.fathom.cloud.compute.api.os.model.actions.RemoveFloatingIpRequest;
import io.fathom.cloud.compute.api.os.model.actions.RemoveSecurityGroupRequest;
import io.fathom.cloud.compute.networks.IpRanges;
import io.fathom.cloud.compute.services.ComputeServices;
import io.fathom.cloud.compute.services.Flavors;
import io.fathom.cloud.compute.services.Instances;
import io.fathom.cloud.compute.services.IpPools;
import io.fathom.cloud.compute.services.MetadataServices;
import io.fathom.cloud.compute.services.SecurityGroups;
import io.fathom.cloud.compute.services.SshKeyPairs;
import io.fathom.cloud.compute.state.ComputeRepository;
import io.fathom.cloud.protobuf.CloudModel.FlavorData;
import io.fathom.cloud.protobuf.CloudModel.InstanceData;
import io.fathom.cloud.protobuf.CloudModel.KeyPairData;
import io.fathom.cloud.protobuf.CloudModel.NetworkAddressData;
import io.fathom.cloud.protobuf.CloudModel.ReservationData;
import io.fathom.cloud.protobuf.CloudModel.SecurityGroupData;
import io.fathom.cloud.server.auth.Auth;
import io.fathom.cloud.server.model.Project;
import io.fathom.cloud.services.ImageService;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.net.InetAddresses;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.inject.persist.Transactional;

@Path("/openstack/compute/{project}/servers")
@Transactional
public class ServersResource extends ComputeResourceBase {
    private static final Logger log = LoggerFactory.getLogger(ServersResource.class);

    @Inject
    SecurityGroups securityGroups;

    @Inject
    Instances instances;

    @Inject
    Flavors flavors;

    @Inject
    Provider<StartInstancesAction> startInstancesActionProvider;

    @Inject
    Provider<StopInstancesAction> stopInstancesActionProvider;

    @Inject
    ComputeRepository instanceStateStore;

    @Inject
    ComputeServices computeServices;

    @Inject
    ImageService imageService;

    @Inject
    SshKeyPairs keypairs;

    @Inject
    Gson gson;

    @Inject
    IpPools ipPools;

    @GET
    @Path("{id}")
    public WrappedServer doServerGet(@PathParam("id") String id) throws CloudException {
        InstanceData instance = getInstance(id);

        WrappedServer response = new WrappedServer();

        ReservationData reservation = getReservation(instance.getReservationId());

        response.server = toModel(reservation, instance, true);
        return response;
    }

    @PUT
    @Path("{id}")
    public WrappedServer doServerPut(@PathParam("id") String id, WrappedServer request) throws CloudException {
        InstanceData instance = getInstance(id);

        if (request.server == null) {
            request.server = new Server();
        }

        if (request.server.name != null && !Objects.equal(instance.getName(), request.server.name)) {
            instance = instances.updateInstance(getProject(), instance.getId(), request.server.name);
        }

        WrappedServer response = new WrappedServer();

        ReservationData reservation = getReservation(instance.getReservationId());

        response.server = toModel(reservation, instance, true);
        return response;
    }

    @POST
    @Path("{id}/action")
    public Response doAction(@PathParam("id") String id, JsonObject jsonRequest) throws CloudException, IOException {
        InstanceData instance = getInstance(id);

        for (Entry<String, JsonElement> entry : jsonRequest.entrySet()) {
            String key = entry.getKey();
            if (key.equals("addFloatingIp")) {
                AddFloatingIpRequest request = gson.fromJson(entry.getValue(), AddFloatingIpRequest.class);

                return addFloatingIp(instance, request);
            } else if (key.equals("removeFloatingIp")) {
                RemoveFloatingIpRequest request = gson.fromJson(entry.getValue(), RemoveFloatingIpRequest.class);

                return removeFloatingIp(instance, request);
            } else if (key.equals("addSecurityGroup")) {
                AddSecurityGroupRequest request = gson.fromJson(entry.getValue(), AddSecurityGroupRequest.class);

                return addRemoveSecurityGroup(instance, request.name, false);
            } else if (key.equals("removeSecurityGroup")) {
                RemoveSecurityGroupRequest request = gson.fromJson(entry.getValue(), RemoveSecurityGroupRequest.class);

                return addRemoveSecurityGroup(instance, request.name, true);
            } else if (key.equals("createImage")) {
                CreateImageRequest request = gson.fromJson(entry.getValue(), CreateImageRequest.class);

                return createImage(instance, request);
            } else {
                throw new IllegalArgumentException("Unknown action: " + key);
            }
        }
        throw new IllegalArgumentException();

    }

    private Response addRemoveSecurityGroup(InstanceData instance, String name, boolean remove) throws CloudException {
        Project project = getProject();

        Long id = Long.valueOf(name);
        SecurityGroupData sg = securityGroups.find(project, id);

        // SecurityGroupData sg = securityGroups.find(project, request.name);
        notFoundIfNull(sg);

        securityGroups.addRemoveSecurityGroup(project, instance.getId(), sg, remove);

        return Response.accepted().build();
    }

    private Response createImage(InstanceData instance, CreateImageRequest request) throws IOException, CloudException {
        Project project = getProject();

        ImageService.Image image = computeServices.createImage(project, instance, request);

        String location = imageService.getUrl(httpRequest, image.getId());

        return Response.ok().header(HttpHeaders.LOCATION, location).build();
    }

    private Response removeFloatingIp(InstanceData instance, RemoveFloatingIpRequest request) throws CloudException {
        Project project = getProject();

        ipPools.detachFloatingIp(project, instance, request);

        return Response.ok().build();
    }

    protected Response addFloatingIp(InstanceData instance, AddFloatingIpRequest request) throws CloudException {
        Project project = getProject();

        ipPools.attachFloatingIp(project, instance, request);

        return Response.ok().build();
    }

    @DELETE
    @Path("{id}")
    public Response shutdownServer(@PathParam("id") String id) throws CloudException {
        InstanceData instance = getInstance(id);

        StopInstancesAction action = stopInstancesActionProvider.get();
        action.project = getProject();
        action.instances = Lists.newArrayList();
        action.instances.add(instance);
        action.go();

        ResponseBuilder response = Response.noContent();
        return response.build();
    }

    @GET
    @Path("{id}/os-volume_attachments")
    public VolumeAttachments listVolumeAttachments(@PathParam("id") String id) throws CloudException {
        VolumeAttachments response = new VolumeAttachments();

        response.volumeAttachments = Lists.newArrayList();

        log.warn("os-volume_attachments is stub-implemented");

        return response;
    }

    @GET
    @Path("{id}/os-security-groups")
    public SecurityGroupList listSecurityGroups(@PathParam("id") String id) throws CloudException {
        Project project = getProject();

        InstanceData instance = getInstance(id);

        SecurityGroupList response = new SecurityGroupList();

        response.securityGroups = Lists.newArrayList();

        for (long sgId : instance.getSecurityGroupIdList()) {
            SecurityGroupData data = securityGroups.find(getProject(), sgId);
            if (data == null) {
                log.warn("Cannot find sg: {}", sgId);
                continue;
            }
            SecurityGroup model = SecurityGroupsResource.toModel(project, data, true);
            response.securityGroups.add(model);
        }

        return response;
    }

    private InstanceData getInstance(String id) throws CloudException {
        InstanceData instance = instanceStateStore.getInstances(getProject().getId()).find(Long.valueOf(id));
        if (instance == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }
        return instance;
    }

    private ReservationData getReservation(long id) throws CloudException {
        ReservationData r = instanceStateStore.getReservations(getProject()).find(id);
        if (r == null) {
            throw new WebApplicationException(Status.NOT_FOUND);
        }
        return r;
    }

    private ServerList listServers(boolean details) throws CloudException {
        Auth auth = getAuth();

        boolean allTenants = httpRequest.getParameter("all_tenants") != null;

        Project filterProject = getProject();
        if (allTenants) {
            filterProject = null;
        }

        List<InstanceData> instances = computeServices.listInstances(auth, filterProject);

        Map<Long, ReservationData> reservations = Maps.newHashMap();

        ServerList response = new ServerList();
        response.servers = Lists.newArrayList();
        for (InstanceData instance : instances) {
            ReservationData reservation = reservations.get(instance.getReservationId());
            if (reservation == null) {
                reservation = getReservation(instance.getReservationId());
                reservations.put(instance.getReservationId(), reservation);
            }
            response.servers.add(toModel(reservation, instance, details));
        }

        return response;
    }

    @GET
    @Path("detail")
    public ServerList listDetails() throws CloudException {
        return listServers(true);
    }

    @GET
    public ServerList list() throws CloudException {
        return listServers(false);
    }

    @POST
    @Produces({ JSON })
    public Response launchServer(WrappedServer request) throws CloudException {
        StartInstancesAction action = startInstancesActionProvider.get();

        // action.user = getUser();
        action.project = getProject();
        action.auth = getAuth();

        FlavorData flavor;
        {
            long flavorId = OpenstackIds.toFlavorId(request.server.flavorRef);
            flavor = flavors.find(flavorId);
            if (flavor == null) {
                throw new IllegalArgumentException();
            }
        }

        action.minCount = request.server.minCount;
        if (action.minCount == 0) {
            action.minCount = 1;
        }
        action.maxCount = request.server.maxCount;
        if (action.maxCount == 0) {
            action.maxCount = 1;
        }

        if (action.maxCount != 1) {
            // Not clear what the response should be in this case...
            throw new UnsupportedOperationException();
        }

        ImageService.Image image;
        {

            long imageId = OpenstackIds.toImageId(request.server.imageRef);
            image = imageService.findImage(getProject(), imageId);
            if (image == null) {
                throw new IllegalArgumentException();
            }
        }

        {
            ReservationData.Builder reservation = ReservationData.newBuilder();
            // TODO: Copy image?
            reservation.setImageId(image.getId());

            action.reservationTemplate = reservation.build();
        }

        {
            InstanceData.Builder instance = InstanceData.newBuilder();
            instance.setName(request.server.name);

            if (request.server.keyName != null) {
                KeyPairData keypair = keypairs.findKeyPair(getProject(), request.server.keyName);
                if (keypair == null) {
                    throw new IllegalArgumentException();
                }
                instance.setKeyPair(keypair);
            }

            instance.setImageId(image.getId());

            instance.setFlavor(flavor);

            if (request.server.securityGroups != null && !request.server.securityGroups.isEmpty()) {
                SecurityGroupDictionary dictionary = new SecurityGroupDictionary(securityGroups.list(getProject()));

                for (SecurityGroup securityGroup : request.server.securityGroups) {
                    String name = securityGroup.name;

                    SecurityGroupData data = dictionary.getByName(name);
                    if (data == null) {
                        throw new IllegalArgumentException("Security group not found: " + name);
                    }

                    instance.addSecurityGroupId(data.getId());
                }
            }

            action.instanceTemplate = instance.build();
        }

        StartInstancesAction.Result result = action.go();

        WrappedServer response = new WrappedServer();

        if (result.instances.size() != 1) {
            throw new IllegalStateException();
        }

        Map<Long, ReservationData> reservations = Maps.newHashMap();

        for (InstanceData instance : result.instances) {
            ReservationData reservation = reservations.get(instance.getReservationId());
            if (reservation == null) {
                reservation = getReservation(instance.getReservationId());
                reservations.put(instance.getReservationId(), reservation);
            }

            response.server = toModel(reservation, instance, false);

            // TODO: adminPass??
        }

        return Response.status(Status.ACCEPTED).entity(response).build();
    }

    private Server toModel(ReservationData reservation, InstanceData instance, boolean details) throws CloudException {
        Server server = new Server();

        server.id = "" + instance.getId();
        server.links = Lists.newArrayList();
        server.name = instance.getName();

        FlavorData flavor;
        if (instance.hasFlavor()) {
            flavor = instance.getFlavor();
        } else {
            // TODO: I don't think any of these escaped publicly... remove.
            // Most clients freak out if we don't have a flavor...
            log.warn("No flavor associated with instance: {}", instance);
            flavor = flavors.list().get(0);
        }

        if (flavor != null) {
            server.flavor = new Flavor();
            server.flavor.id = "" + flavor.getId();
        }

        server.tenantId = "" + instance.getProjectId();

        if (details) {
            server.progress = 0;
            // server.extensionTaskState = "";
            server.extensionPowerState = 1;

            server.created = new Date(instance.getLaunchTime());

            server.addresses = new Addresses();
            server.addresses.privateAddresses = Lists.newArrayList();
            server.addresses.publicAddresses = Lists.newArrayList();

            server.image = new Image();
            server.image.id = "" + instance.getImageId();

            server.metadata = MetadataServices.toMap(instance.getMetadata());

            if (instance.hasNetwork()) {
                InetAddress bestIpv4 = null;
                InetAddress bestIpv6 = null;

                for (NetworkAddressData addressData : instance.getNetwork().getAddressesList()) {
                    String addressString = addressData.getIp();
                    InetAddress address = InetAddresses.forString(addressString);

                    Address xml = new Address();
                    xml.address = addressString;

                    boolean publicAddress = true;
                    if (address instanceof Inet4Address) {
                        xml.version = 4;

                        if (bestIpv4 == null) {
                            bestIpv4 = address;
                        }
                    } else {
                        xml.version = 6;

                        if (bestIpv6 == null) {
                            bestIpv6 = address;
                        }
                    }

                    if (publicAddress) {
                        server.addresses.publicAddresses.add(xml);
                    } else {
                        server.addresses.privateAddresses.add(xml);
                    }
                }

                if (bestIpv4 != null) {
                    boolean publishIpv4 = true;
                    if (!IpRanges.isPublic(bestIpv4) && bestIpv6 != null) {
                        publishIpv4 = false;
                    }

                    if (publishIpv4) {
                        server.accessIPv4 = InetAddresses.toAddrString(bestIpv4);
                    }
                }

                if (bestIpv6 != null) {
                    server.accessIPv6 = InetAddresses.toAddrString(bestIpv6);
                }
            }

            switch (instance.getInstanceState()) {
            case TERMINATED:
            case STOPPED:
                server.status = "DELETED";
                break;

            case RUNNING:
            case SHUTTING_DOWN:
            case STOPPING:
                server.status = "ACTIVE";
                break;

            case PENDING:
                server.status = "ERROR";
                // server.status = "BUILD";
                break;

            default:
                log.warn("Unhandled state {}", instance.getInstanceState());

                server.status = "UNKNOWN";
                break;
            }

            // "accessIPv4": "",
            // "accessIPv6": "",
            // "addresses": {
            // "private": [
            // {
            // "addr": "192.168.0.3",
            // "version": 4
            // }
            // ]
            // },
            // "created": "2012-09-07T16:56:37Z",
            // "flavor": {
            // "id": "1",
            // "links": [
            // {
            // "href": "http://openstack.example.com/openstack/flavors/1",
            // "rel": "bookmark"
            // }
            // ]
            // },
            // "hostId":
            // "16d193736a5cfdb60c697ca27ad071d6126fa13baeb670fc9d10645e",
            // "id": "05184ba3-00ba-4fbc-b7a2-03b62b884931",
            // "image": {
            // "id": "70a599e0-31e7-49b7-b260-868f441e862b",
            // "links": [
            // {
            // "href":
            // "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
            // "rel": "bookmark"
            // }
            // ]
            // },
            // "links": [
            // {
            // "href":
            // "http://openstack.example.com/v2/openstack/servers/05184ba3-00ba-4fbc-b7a2-03b62b884931",
            // "rel": "self"
            // },
            // {
            // "href":
            // "http://openstack.example.com/openstack/servers/05184ba3-00ba-4fbc-b7a2-03b62b884931",
            // "rel": "bookmark"
            // }
            // ],
            // "metadata": {
            // "My Server Name": "Apache1"
            // },
            // "name": "new-server-test",
            // "progress": 0,
            // "status": "ACTIVE",
            // "tenant_id": "openstack",
            // "updated": "2012-09-07T16:56:37Z",
            // "user_id": "fake"
        }
        return server;
    }

}
TOP

Related Classes of io.fathom.cloud.compute.api.os.resources.ServersResource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.