brooklyn.networking.cloudstack.portforwarding.CloudstackPortForwarder.java Source code

Java tutorial

Introduction

Here is the source code for brooklyn.networking.cloudstack.portforwarding.CloudstackPortForwarder.java

Source

/*
 * Copyright 2013-2015 by Cloudsoft Corporation Limited
 *
 * Licensed 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 brooklyn.networking.cloudstack.portforwarding;

import static java.lang.String.format;

import java.util.List;
import java.util.Map;

import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.MachineLocation;
import org.apache.brooklyn.api.location.PortRange;
import org.apache.brooklyn.api.mgmt.ManagementContext;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.location.access.PortForwardManager;
import org.apache.brooklyn.location.jclouds.JcloudsLocation;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.net.Cidr;
import org.apache.brooklyn.util.net.HasNetworkAddresses;
import org.apache.brooklyn.util.net.Protocol;
import org.apache.brooklyn.util.text.Strings;
import org.jclouds.cloudstack.domain.NIC;
import org.jclouds.cloudstack.domain.PortForwardingRule;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.VirtualMachine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.net.HostAndPort;

import brooklyn.networking.cloudstack.CloudstackNew40FeaturesClient;
import brooklyn.networking.common.subnet.PortForwarder;
import brooklyn.networking.subnet.SubnetTier;

public class CloudstackPortForwarder implements PortForwarder {

    private static final Logger log = LoggerFactory.getLogger(CloudstackPortForwarder.class);

    public static final ConfigKey<String> DEFAULT_GATEWAY = ConfigKeys.newStringConfigKey("default.gateway",
            "Default gateway IP for public traffic", "10.255.129.1");

    public static final ConfigKey<Boolean> USE_VPC = ConfigKeys
            .newBooleanConfigKey("advancednetworking.cloudstack.forwader.useVpc", "Whether to use VPC's", false);

    public static final ConfigKey<String> PUBLIC_IP_ID = ConfigKeys.newStringConfigKey(
            "advancednetworking.cloudstack.forwader.publicIpId",
            "The public IP id to use (if null, will attempt to acquire one)", null);

    private final Object mutex = new Object();
    private PortForwardManager portForwardManager;
    private CloudstackNew40FeaturesClient client;
    private SubnetTier subnetTier;
    private JcloudsLocation jcloudsLocation;

    public CloudstackPortForwarder() {

    }

    public CloudstackPortForwarder(PortForwardManager portForwardManager) {
        this.portForwardManager = portForwardManager;
    }

    @Override
    public void setManagementContext(ManagementContext managementContext) {
        if (portForwardManager == null) {
            portForwardManager = (PortForwardManager) managementContext.getLocationRegistry()
                    .resolve("portForwardManager(scope=global)");
        }
    }

    @Override
    public PortForwardManager getPortForwardManager() {
        return portForwardManager;
    }

    @Override
    public void inject(Entity owner, List<Location> locations) {
        subnetTier = (SubnetTier) owner;
        jcloudsLocation = (JcloudsLocation) Iterables.get(locations, 0);
        client = CloudstackNew40FeaturesClient.newInstance(jcloudsLocation);
    }

    @Override
    public String openGateway() {
        return subnetTier.getConfig(DEFAULT_GATEWAY);
    }

    @Override
    public String openStaticNat(Entity serviceToOpen) {
        //TODO: add a static NAT config key to enable static NAT allocation.
        throw new UnsupportedOperationException();
    }

    @Override
    public void openFirewallPort(Entity entity, int port, Protocol protocol, Cidr accessingCidr) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void openFirewallPortRange(Entity entity, PortRange portRange, Protocol protocol, Cidr accessingCidr) {
        throw new UnsupportedOperationException();
    }

    public HostAndPort openPortForwarding(MachineLocation targetVm, int targetPort,
            Optional<Integer> optionalPublicPort, Protocol protocol, Cidr accessingCidr) {
        Preconditions.checkNotNull(client);

        return openPortForwarding((HasNetworkAddresses) targetVm, targetPort, optionalPublicPort, protocol,
                accessingCidr);
    }

    @Override
    public HostAndPort openPortForwarding(HasNetworkAddresses targetVm, int targetPort,
            Optional<Integer> optionalPublicPort, Protocol protocol, Cidr accessingCidr) {
        Preconditions.checkNotNull(client);
        final String ipAddress = String.valueOf(targetVm.getPrivateAddresses().toArray()[0]);
        Maybe<VirtualMachine> vm = client.findVmByIp(ipAddress);
        Boolean useVpc = subnetTier.getConfig(USE_VPC);
        String publicIpId = subnetTier.getConfig(PUBLIC_IP_ID);

        if (vm.isAbsentOrNull()) {
            Map<VirtualMachine, List<String>> vmIpMapping = client.getVmIps();
            log.error("Could not find any VMs with Ip Address {}; contenders: {}", ipAddress, vmIpMapping);
            return null;
        }
        NIC nic = Iterables.find(vm.get().getNICs(), new Predicate<NIC>() {
            @Override
            public boolean apply(NIC input) {
                return (input == null) ? false : ipAddress.equals(input.getIPAddress());
            }
        });
        String networkId = nic.getNetworkId();

        Maybe<String> vpcId;
        if (useVpc) {
            vpcId = client.findVpcIdFromNetworkId(networkId);

            if (vpcId.isAbsent()) {
                log.error(
                        "Could not find associated VPCs with Network: {}; continuing without opening port-forwarding",
                        networkId);
                return null;
            }
        } else {
            vpcId = Maybe.absent("use-vpc not enabled");
        }

        try {
            synchronized (mutex) {
                Maybe<PublicIPAddress> allocatedPublicIpAddressId = client
                        .findPublicIpAddressByVmId(vm.get().getId());
                PublicIPAddress publicIpAddress;

                if (allocatedPublicIpAddressId.isPresent()) {
                    publicIpAddress = allocatedPublicIpAddressId.get();
                } else if (Strings.isNonBlank(publicIpId)) {
                    publicIpAddress = client.getCloudstackGlobalClient().getAddressApi()
                            .getPublicIPAddress(publicIpId);
                    if (publicIpAddress == null) {
                        throw new IllegalStateException("No public-ip found with id " + publicIpAddress);
                    }
                } else if (useVpc) {
                    publicIpAddress = client.createIpAddressForVpc(vpcId.get());
                } else {
                    publicIpAddress = client.createIpAddressForNetwork(networkId);
                }

                int publicPort;
                if (optionalPublicPort.isPresent()) {
                    publicPort = optionalPublicPort.get();
                } else {
                    PortForwardManager pfw = getPortForwardManager();
                    publicPort = pfw.acquirePublicPort(publicIpAddress.getId());
                }

                log.info(format("Opening port:%s on vm:%s with IP:%s", targetPort, vm.get().getId(),
                        publicIpAddress.getIPAddress()));
                String jobid = client.createPortForwardRule(networkId, publicIpAddress.getId(),
                        PortForwardingRule.Protocol.TCP, publicPort, vm.get().getId(), targetPort);
                client.waitForJobSuccess(jobid);
                log.debug("Enabled port-forwarding on {}", publicIpAddress.getIPAddress() + ":" + publicPort);
                return HostAndPort.fromParts(publicIpAddress.getIPAddress(), publicPort);
            }
        } catch (Exception e) {
            log.error("Failed creating port forwarding rule on " + this + " to " + targetPort + "; continuing", e);
            // it might already be created, so don't crash and burn too hard!
            return null;
        }
    }

    @Override
    public HostAndPort openPortForwarding(HostAndPort target, Optional<Integer> optionalPublicPort,
            Protocol protocol, Cidr accessingCidr) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean closePortForwarding(HostAndPort targetSide, HostAndPort publicSide, Protocol protocol) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean closePortForwarding(HasNetworkAddresses machine, int targetPort, HostAndPort publicSide,
            Protocol protocol) {
        // TODO Need support for client.deletePortForwardRuleForVpc(...)
        // Until then, no-op
        return false;
    }

    @Override
    public boolean isClient() {
        return false;
    }
}