/* * 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.brooklyn.core.location.access; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.net.HostAndPort; import java.util.Collection; /** * Acts as a registry for existing port mappings (e.g. the public endpoints for accessing specific * ports on private VMs). This could be using DNAT, or iptables port-forwarding, or Docker port-mapping * via the host, or any other port mapping approach. * * Also controls the allocation of ports via {@link #acquirePublicPort(String)} * (e.g. for port-mapping with DNAT, then which port to use for the public side). * * Implementations typically will not know anything about what the firewall/IP actually is, they just * handle a unique identifier for it. * * To use, see {@link PortForwardManagerLocationResolver}, with code such as * {@code managementContext.getLocationRegistry().resolve("portForwardManager(scope=global)")}. * * @see PortForwardManagerImpl for implementation notes and considerations. */ @Beta public interface PortForwardManager extends Location { @Beta class AssociationMetadata { private final String publicIpId; private final HostAndPort publicEndpoint; private final Location location; private final int privatePort; /** * Users are discouraged from calling this constructor; the signature may change in future releases. * Instead, instances will be created automatically by Brooklyn to be passed to the * {@link AssociationListener#onAssociationCreated(AssociationMetadata)} method. */ public AssociationMetadata(String publicIpId, HostAndPort publicEndpoint, Location location, int privatePort) { this.publicIpId = publicIpId; this.publicEndpoint = publicEndpoint; this.location = location; this.privatePort = privatePort; } public String getPublicIpId() { return publicIpId; } public HostAndPort getPublicEndpoint() { return publicEndpoint; } public Location getLocation() { return location; } public int getPrivatePort() { return privatePort; } public String toString() { return Objects.toStringHelper(this) .add("publicIpId", publicIpId) .add("publicEndpoint", publicEndpoint) .add("location", location) .add("privatePort", privatePort) .toString(); } } @Beta interface AssociationListener { void onAssociationCreated(AssociationMetadata metadata); void onAssociationDeleted(AssociationMetadata metadata); } /** * The intention is that there is one PortForwardManager instance per "scope". If you * use global, then it will be a shared instance (for that management context). If you * pass in your own name (e.g. "docker-fjie3") then it will shared with just any other * places that use that same location spec (e.g. {@code portForwardManager(scope=docker-fjie3)}). */ // TODO Note: using name "scope" rather than "brooklyn.portForwardManager.scope" so that location spec // "portForwardManager(scope=global)" works, rather than having to do // portForwardManager(brooklyn.portForwardManager.scope=global). // The config being read by the PortForwardManagerLocationResolver doesn't respect @SetFromFlag("scope"). public static final ConfigKey<String> SCOPE = ConfigKeys.newStringConfigKey( "scope", "The scope that this applies to, defaulting to global", "global"); @Beta public static final ConfigKey<Integer> PORT_FORWARD_MANAGER_STARTING_PORT = ConfigKeys.newIntegerConfigKey( "brooklyn.portForwardManager.startingPort", "The starting port for assigning port numbers, such as for DNAT", 11000); public String getScope(); /** * Reserves a unique public port on the given publicIpId. * <p> * Often followed by {@link #associate(String, HostAndPort, int)} or {@link #associate(String, HostAndPort, Location, int)} * to enable {@link #lookup(String, int)} or {@link #lookup(Location, int)} respectively. */ public int acquirePublicPort(String publicIpId); /** * Records a location and private port against a public endpoint (ip and port), * to support {@link #lookup(Location, int)}. * <p> * Superfluous if {@link #acquirePublicPort(String, Location, int)} was used, * but strongly recommended if {@link #acquirePublicPortExplicit(String, int)} was used * e.g. if the location is not known ahead of time. */ public void associate(String publicIpId, HostAndPort publicEndpoint, Location l, int privatePort); /** * Records a mapping for publicIpId:privatePort to a public endpoint, such that it can * subsequently be looked up using {@link #lookup(String, int)}. */ public void associate(String publicIpId, HostAndPort publicEndpoint, int privatePort); /** * Registers a listener, which will be notified each time a new port mapping is associated. See {@link #associate(String, HostAndPort, int)} * and {@link #associate(String, HostAndPort, Location, int)}. */ @Beta public void addAssociationListener(AssociationListener listener, Predicate<? super AssociationMetadata> filter); @Beta public void removeAssociationListener(AssociationListener listener); /** * Returns the public ip hostname and public port for use contacting the given endpoint. * <p> * Will return null if: * <ul> * <li>No publicPort is associated with this location and private port. * <li>No publicIpId is associated with this location and private port. * <li>No publicIpHostname is recorded against the associated publicIpId. * </ul> * Conceivably this may have to be access-location specific. * * @see #recordPublicIpHostname(String, String) */ public HostAndPort lookup(Location l, int privatePort); /** * Returns the public endpoint (host and port) for use contacting the given endpoint. * * Expects a previous call to {@link #associate(String, HostAndPort, int)}, to register * the endpoint. * * Will return null if there has not been a public endpoint associated with this pairing. */ public HostAndPort lookup(String publicIpId, int privatePort); /** * Clears the given port mapping, returning true if there was a match. */ public boolean forgetPortMapping(String publicIpId, int publicPort); /** * Clears the port mappings associated with the given location, returning true if there were any matches. */ public boolean forgetPortMappings(Location location); /** * Clears the port mappings associated with the given publicIpId, returning true if there were any matches. */ public boolean forgetPortMappings(String publicIpId); public String toVerboseString(); /////////////////////////////////////////////////////////////////////////////////// // Deprecated /////////////////////////////////////////////////////////////////////////////////// /** * Reserves a unique public port for the purpose of forwarding to the given target, * associated with a given location for subsequent lookup purpose. * <p> * If already allocated, returns the previously allocated. * * @deprecated since 0.7.0; use {@link #acquirePublicPort(String)}, and then use {@link #associate(String, HostAndPort, int)} or {@link #associate(String, HostAndPort, Location, int)} */ @Deprecated public int acquirePublicPort(String publicIpId, Location l, int privatePort); /** * Returns old mapping if it existed, null if it is new. * * @deprecated since 0.7.0; use {@link #associate(String, HostAndPort, int)} or {@link #associate(String, HostAndPort, Location, int)} */ @Deprecated public PortMapping acquirePublicPortExplicit(String publicIpId, int port); /** * Records a location and private port against a publicIp and public port, * to support {@link #lookup(Location, int)}. * <p> * Superfluous if {@link #acquirePublicPort(String, Location, int)} was used, * but strongly recommended if {@link #acquirePublicPortExplicit(String, int)} was used * e.g. if the location is not known ahead of time. * * @deprecated Use {@link #associate(String, HostAndPort, Location, int)} */ @Deprecated public void associate(String publicIpId, int publicPort, Location l, int privatePort); /** * Records a public hostname or address to be associated with the given publicIpId for lookup purposes. * <p> * Conceivably this may have to be access-location specific. * * @deprecated Use {@link #associate(String, HostAndPort, int)} or {@link #associate(String, HostAndPort, Location, int)} */ @Deprecated public void recordPublicIpHostname(String publicIpId, String hostnameOrPublicIpAddress); /** * Returns a recorded public hostname or address. * * @deprecated Use {@link #lookup(String, int)} or {@link #lookup(Location, int)} */ @Deprecated public String getPublicIpHostname(String publicIpId); /** * Clears a previous call to {@link #recordPublicIpHostname(String, String)}. * * @deprecated Use {@link #forgetPortMapping(String, int)} or {@link #forgetPortMappings(Location)} */ @Deprecated public boolean forgetPublicIpHostname(String publicIpId); /** * Returns true if this implementation is a client which is immutable/safe for serialization * i.e. it delegates to something on an entity or location elsewhere. * * @deprecated since 0.7.0; no need to separate client-proxy from impl */ @Deprecated public boolean isClient(); /////////////////////////////////////////////////////////////////////////////////// // Deprecated; just internal /////////////////////////////////////////////////////////////////////////////////// /** * Returns the port mapping for a given publicIpId and public port. * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public PortMapping getPortMappingWithPublicSide(String publicIpId, int publicPort); /** * Returns the subset of port mappings associated with a given public IP ID. * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public Collection<PortMapping> getPortMappingWithPublicIpId(String publicIpId); /** * @see {@link #forgetPortMapping(String, int)} and {@link #forgetPortMappings(Location)} * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public boolean forgetPortMapping(PortMapping m); /** * Returns the public host and port for use accessing the given mapping. * <p> * Conceivably this may have to be access-location specific. * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public HostAndPort getPublicHostAndPort(PortMapping m); /** * Returns the subset of port mappings associated with a given location. * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public Collection<PortMapping> getLocationPublicIpIds(Location l); /** * Returns the mapping to a given private port, or null if none. * * @deprecated since 0.7.0; this method will be internal only */ @Deprecated public PortMapping getPortMappingWithPrivateSide(Location l, int privatePort); }