Repository: stratos Updated Branches: refs/heads/master 39162a58d -> efd079d06
introducing openstack networking abstraction and implementations Project: http://git-wip-us.apache.org/repos/asf/stratos/repo Commit: http://git-wip-us.apache.org/repos/asf/stratos/commit/efd079d0 Tree: http://git-wip-us.apache.org/repos/asf/stratos/tree/efd079d0 Diff: http://git-wip-us.apache.org/repos/asf/stratos/diff/efd079d0 Branch: refs/heads/master Commit: efd079d0673505b3f0781f9066fb37cc87de777e Parents: 39162a5 Author: R-Rajkumar <[email protected]> Authored: Thu Dec 4 21:16:12 2014 +0530 Committer: R-Rajkumar <[email protected]> Committed: Thu Dec 4 21:16:12 2014 +0530 ---------------------------------------------------------------------- .../networking/NeutronNetworkingApi.java | 694 +++++++++++++++++++ .../openstack/networking/NovaNetworkingApi.java | 257 +++++++ .../networking/OpenstackNetworkingApi.java | 60 ++ .../util/CloudControllerConstants.java | 1 + 4 files changed, 1012 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/stratos/blob/efd079d0/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NeutronNetworkingApi.java ---------------------------------------------------------------------- diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NeutronNetworkingApi.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NeutronNetworkingApi.java new file mode 100644 index 0000000..6dcbda9 --- /dev/null +++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NeutronNetworkingApi.java @@ -0,0 +1,694 @@ +/* + * 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.stratos.cloud.controller.iaases.openstack.networking; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.stratos.cloud.controller.exception.CloudControllerException; +import org.apache.stratos.cloud.controller.domain.FloatingNetwork; +import org.apache.stratos.cloud.controller.domain.FloatingNetworks; +import org.apache.stratos.cloud.controller.domain.IaasProvider; +import org.apache.stratos.cloud.controller.domain.NetworkInterface; +import org.apache.stratos.cloud.controller.util.CloudControllerConstants; +import org.apache.stratos.cloud.controller.util.CloudControllerUtil; +import org.apache.stratos.cloud.controller.util.ComputeServiceBuilderUtil; +import org.jclouds.ContextBuilder; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.logging.slf4j.config.SLF4JLoggingModule; +import org.jclouds.openstack.neutron.v2.NeutronApi; +import org.jclouds.openstack.neutron.v2.domain.FloatingIP; +import org.jclouds.openstack.neutron.v2.domain.IP; +import org.jclouds.openstack.neutron.v2.domain.Port; +import org.jclouds.openstack.neutron.v2.extensions.FloatingIPApi; +import org.jclouds.openstack.neutron.v2.features.PortApi; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.net.InetAddresses; +import com.google.inject.Module; + +/** + * Leveraging openstack-neutron networking apis. + * + * @author rajkumar + */ +public class NeutronNetworkingApi implements OpenstackNetworkingApi { + + private static final Log log = LogFactory.getLog(NeutronNetworkingApi.class); + + private final String provider = "openstack-neutron"; + private IaasProvider iaasProvider; + private NeutronApi neutronApi; + private PortApi portApi; + private FloatingIPApi floatingIPApi; + + public NeutronNetworkingApi(IaasProvider iaasProvider) { + String iaasProviderNullMsg = String.format("Iaas provider is null. Unable to create an instance of %s", + NeutronNetworkingApi.class.getName()); + assertNotNull(iaasProvider, iaasProviderNullMsg); + this.iaasProvider = iaasProvider; + } + + @Override + public List<String> associateAddresses(NodeMetadata node) { + + assertNotNull(node, "Node cannot be null"); + + if (null == neutronApi || null == portApi || null == floatingIPApi) { + buildNeutronApi(); + } + + // internal network uuid to floating networks map, as defined in cartridge definition + Map<String, List<FloatingNetwork>> networkUuidToFloatingNetworksMap = + getNetworkUuidToFloatingNetworksMap(iaasProvider.getNetworkInterfaces()); + + // private IP to floating networks map, as defined in cartridge definition + Map<String, List<FloatingNetwork>> fixedIPToFloatingNetworksMap = + getFixedIPToFloatingNetworksMap(iaasProvider.getNetworkInterfaces()); + + // list of IPs allocated to this node + List<String> associatedFlotingIPs = new ArrayList<String>(); + + // wait until node gets private IPs + while (node.getPrivateAddresses() == null) { + CloudControllerUtil.sleep(1000); + } + + // loop through all the fixed IPs of this node + // and see whether we need to assign floating IP to each according to the cartridge deployment + for (String privateIPOfTheNode : node.getPrivateAddresses()) { + Port portOfTheFixedIP = getPortByFixedIP(privateIPOfTheNode); + if (null == portOfTheFixedIP) { + // we can't assign floating IP if port is null + // it can't happen, a fixed/private IP can't live without a port + // but doing a null check to be on the safe side + if (log.isDebugEnabled()) { + String msg = String.format("Port not found for fixed IP %s", privateIPOfTheNode); + log.debug(msg); + } + continue; + } + // get list of floating networks associated with each network interfaces (refer cartridge definition) + List<FloatingNetwork> floatingNetworks = networkUuidToFloatingNetworksMap.get(portOfTheFixedIP.getNetworkId()); + // if no floating networks is defined for a network interface, no need to assign any floating IPs, skip the current iteration + if (null == floatingNetworks || floatingNetworks.isEmpty()) { + // since no floating networks found in networkUuidToFloatingNetworksMap, + // we will search in fixedIPToFloatingNetworksMap + floatingNetworks = fixedIPToFloatingNetworksMap.get(privateIPOfTheNode); + if (null == floatingNetworks || floatingNetworks.isEmpty()) { + if (log.isDebugEnabled()) { + String msg = String.format("No floating networks defined for the network interface %s", + portOfTheFixedIP.getNetworkId()); + log.debug(msg); + } + } + continue; + } + // if floating networks defined for a network interface, assign one floating IP from each floating network + for (FloatingNetwork floatingNetwork : floatingNetworks) { + FloatingIP allocatedFloatingIP = null; + if (floatingNetwork.getNetworkUuid() != null && !floatingNetwork.getNetworkUuid().isEmpty()) { + allocatedFloatingIP = assignFloatingIP(portOfTheFixedIP, floatingNetwork.getNetworkUuid()); + } else if (floatingNetwork.getFloatingIP() != null && !floatingNetwork.getFloatingIP().isEmpty()) { + allocatedFloatingIP = assignPredefinedFloatingIP(portOfTheFixedIP, floatingNetwork.getFloatingIP()); + } else { + String msg = String.format("Neither floating network uuid or floating IP defined for the floating network %s", + floatingNetwork.getName()); + log.error(msg); + throw new CloudControllerException(msg); + } + + String allocatedFloatingIPNullMsg = String.format("Error occured while assigning floating IP. " + + "Please check whether the floating network %s can be reached from the fixed IP range", floatingNetwork.getNetworkUuid()); + assertNotNull(allocatedFloatingIP, allocatedFloatingIPNullMsg); + + String allocatedFloatingIPAddressNullOrEmptyMsg = String.format("Error occured while assigning floating IP. " + + "Please check whether the floating network %s can be reached from the fixed IP range", floatingNetwork.getNetworkUuid()); + assertNotNullAndNotEmpty(allocatedFloatingIP.getFloatingIpAddress(), allocatedFloatingIPAddressNullOrEmptyMsg); + + associatedFlotingIPs.add(allocatedFloatingIP.getFloatingIpAddress()); + } + } + return associatedFlotingIPs; + } + + @Override + public String associatePredefinedAddress(NodeMetadata node, String ip) { + // we are not considering about the predefined floating IP which are + // defined in property section of the cartridge definition + // because it doesn't make sense when we are having so many interfaces defined. + // so we don't know which interface to be assigned with this ip + // so in neutron network case, we are only considering the predefined floating IPs defined in floating networks section + // so we should not call this API for neutron networks + return null; + } + + @Override + public void releaseAddress(String ip) { + + String iPNotValidMsg = String.format("Unable to release the IP. The given IP is not valid", ip); + assertValidIP(ip, iPNotValidMsg); + + if (null == neutronApi || null == portApi || null == floatingIPApi) { + buildNeutronApi(); + } + + if (log.isDebugEnabled()) { + String msg = String.format("Trying delete the floating IP %s", ip); + log.debug(msg); + } + + FloatingIP floatingIP = getFloatingIPByIPAddress(ip); + if (null == floatingIP) { + if (log.isDebugEnabled()) { + String msg = String.format("Floating IP %s is not found. " + + "It might be already deleted, if instance is already terminated", ip); + log.debug(msg); + } + return; + } + + boolean deleted = floatingIPApi.delete(floatingIP.getId()); + if (deleted) { + if (log.isDebugEnabled()) { + String msg = String.format("Successfully deleted the floating IP %s", ip); + log.debug(msg); + } + } else { + String msg = String.format("Couldn't release the floating IP %s", ip); + log.error(msg); + throw new CloudControllerException(msg); + } + } + + /** + * Assign a {@link FloatingIP} from the given {@link FloatingNetwork} to the given {@link Port}. + * It will either assign an existing floating IP + * or it will create and assign a new floating IP. + * + * @param port the {@link Port} to which a floating IP to be assigned. + * @param floatingNetworkUuid the network uuid of the floating network + * from which a floating IP to be chosen/created + * @return the assigned Floating IP + */ + private FloatingIP assignFloatingIP(Port port, String floatingNetworkUuid) { + // checking whether if there are any available floating IPs in the external network + // if there are any we don't need to create a new one + ArrayList<FloatingIP> unassignedFloatingIPs = getUnassignedFloatingIPsByNetworkUuid(floatingNetworkUuid); + + // we should remove all predefined floating IPs from unassigned list + // otherwise, these predefined floating IPs can be associated to some other interfaces + if (unassignedFloatingIPs != null) { + if (log.isDebugEnabled()) { + String msg = String.format("Unassigned floating IPs from the network %s - %s", + floatingNetworkUuid, unassignedFloatingIPs.toString()); + log.debug(msg); + } + Iterator<FloatingIP> unassginedFloatingIPsIterator = unassignedFloatingIPs.iterator(); + while (unassginedFloatingIPsIterator.hasNext()) { + FloatingIP floatingIP = unassginedFloatingIPsIterator.next(); + List<String> allPredefinedFloatingIPs = getAllPredefinedFloatingIPs(iaasProvider.getNetworkInterfaces()); + if (allPredefinedFloatingIPs != null) { + if (log.isDebugEnabled()) { + String msg = String.format("Predefined floating IPs - %s found in cartridge", + allPredefinedFloatingIPs.toString()); + log.debug(msg); + } + Iterator<String> predefinedFloatingIPsIterator = allPredefinedFloatingIPs.iterator(); + while (predefinedFloatingIPsIterator.hasNext()) { + String floatingIPAddress = predefinedFloatingIPsIterator.next(); + if (floatingIP.getFloatingIpAddress() != null + && floatingIP.getFloatingIpAddress().equals(floatingIPAddress)) { + unassginedFloatingIPsIterator.remove(); + if (log.isDebugEnabled()) { + String msg = String.format("Removed predefined floating IP %s from available floating IPs", + floatingIPAddress); + log.debug(msg); + } + } + } + } + } + } + + if (unassignedFloatingIPs == null || unassignedFloatingIPs.isEmpty()) { + return createAndAssignFloatingIP(port, floatingNetworkUuid); + } + + if (log.isDebugEnabled()) { + String msg = String.format("Available floating IPs from the network %s - %s", + floatingNetworkUuid, unassignedFloatingIPs.toString()); + log.debug(msg); + } + + // shuffle and get the last for randomness + Collections.shuffle(unassignedFloatingIPs); + FloatingIP selectedFloatingIP = Iterables.getLast(unassignedFloatingIPs); + + if (log.isDebugEnabled()) { + String msg = String.format("Floating IP %s is selected among %s from the network %s", + selectedFloatingIP.getFloatingIpAddress(), unassignedFloatingIPs.toString(), floatingNetworkUuid); + log.debug(msg); + } + + return updateFloatingIP(selectedFloatingIP, port); + } + + /** + * Assign a given FloatingIP address to the given {@link Port} + * It will verify that the given FloatingIP address is actually exists + * and it is not allocated to any of the port + * + * @param port the {@link Port} to which the given FloatingIP address to be assigned + * @param ip predefined floating IP address to be assigned + * @return assigned {@link FloatingIP} + */ + private FloatingIP assignPredefinedFloatingIP(Port port, String predefinedFloatingIP) { + + String invalidIPMsg = String.format("Invalid predefined floating IP %s", predefinedFloatingIP); + assertValidIP(predefinedFloatingIP, invalidIPMsg); + assertNotNull(port, "Invalid port. Port cannot be null"); + + FloatingIP floatingIP = getFloatingIPByIPAddress(predefinedFloatingIP); + + String floatingIPNullMsg = String.format("No such available floating IP %s found", predefinedFloatingIP); + assertNotNull(floatingIP, floatingIPNullMsg); + + ArrayList<FloatingIP> availableFloatingIPs = getUnassignedFloatingIPs(); + FloatingIP updatedFloatingIP = null; + if (availableFloatingIPs.contains(floatingIP)) { + updatedFloatingIP = updateFloatingIP(floatingIP, port); + } else { + String msg = String.format("Predefined floating IP %s is either already allocated to another port %s or unavilable", + predefinedFloatingIP, floatingIP.getPortId()); + log.error(msg); + throw new CloudControllerException(msg); + } + + return updatedFloatingIP; + } + + /** + * Assign the given Floating IP to the given port. + * + * @param floatingIP the Floating IP to be assigned + * @param portTobeAssigned the port to which the given Floating IP to be assigned + * @return the updated {@link FloatingIP} + */ + private FloatingIP updateFloatingIP(FloatingIP floatingIP, Port portTobeAssigned) { + + assertNotNull(floatingIP, "Cannot update floating IP. Given floating IP is null"); + String portNotNullMsg = String.format("Cannot update floating IP %s. Given port is null", + floatingIP.getFloatingIpAddress()); + assertNotNull(portTobeAssigned, portNotNullMsg); + + FloatingIP updatedFloatingIP = null; + if (log.isDebugEnabled()) { + String msg = String.format("Trying to assign existing floating IP %s to the port %s", + floatingIP.getFloatingIpAddress(), portTobeAssigned.getId()); + log.debug(msg); + } + + try { + updatedFloatingIP = floatingIPApi.update(floatingIP.getId(), FloatingIP.UpdateFloatingIP.updateBuilder() + .portId(portTobeAssigned.getId()) + .fixedIpAddress(portTobeAssigned.getFixedIps().iterator().next().getIpAddress()) + .build()); + } catch (Exception e) { + String msg = String.format("Error while trying to assign existing floating IP %s to the port %s", + floatingIP.toString(), portTobeAssigned.toString()); + log.error(msg, e); + throw new CloudControllerException(msg, e); + } + + String updatedFloatingIPNullMessage = String.format("Unable to assign existing floating IP %s " + + "to the port %s", floatingIP.toString(), portTobeAssigned.toString()); + assertNotNull(updatedFloatingIP, updatedFloatingIPNullMessage); + + if (log.isDebugEnabled()) { + String msg = String.format("Successfully updated the floating IP %s", floatingIP.toString()); + log.debug(msg); + } + return updatedFloatingIP; + } + + /** + * Create a new Floating IP from the given floating network and assign it to the given port + * + * @param port the port to which a Floating IP to be assigned + * @param floatingNetworkUuid the network uuid of + * the floating network from which the Floating IP should be created + * @return the newly created/assigned {@link FloatingIP} + */ + private FloatingIP createAndAssignFloatingIP(Port port, String floatingNetworkUuid) { + + assertNotNull(port, "Cannot create floating IP. Invalid port. Port cannot be null"); + assertNotNullAndNotEmpty(floatingNetworkUuid, "Cannot create floating IP. Invalid floating network uuid. " + + "Floating network uuid cannot be null"); + + if (log.isDebugEnabled()) { + String msg = String.format("Trying to create a floating IP from network %s to assign to the port %s", + floatingNetworkUuid, port.getId()); + log.debug(msg); + } + + FloatingIP.CreateFloatingIP createFip; + try { + createFip = FloatingIP.createBuilder(floatingNetworkUuid).portId(port.getId()).build(); + } catch (Exception e) { + String msg = String.format("Error while getting floating IP builder for the external network %s and port %s", + floatingNetworkUuid, port.toString()); + log.error(msg, e); + throw new CloudControllerException(msg, e); + } + + FloatingIP floatingIP = null; + try { + floatingIP = floatingIPApi.create(createFip); + } catch (Exception e) { + String msg = String.format("Error while creating floating IP for the port %s, from floating network %s", + port.toString(), floatingNetworkUuid); + log.error(msg, e); + throw new CloudControllerException(msg, e); + } + + String msg = String.format("Unable to create a floting IP from network %s", floatingNetworkUuid); + assertNotNull(floatingIP, msg); + + return floatingIP; + } + + /** + * Get all unassigned Floating IPs from the given floating network. + * To get all unassigned Floating IPs from all floating networks + * use {@link #getUnassignedFloatingIPs()} + * + * @param networkUuid the network uuid of the floating network + * @return list of all unassigned {@link FloatingIP} from the given floating network + */ + private ArrayList<FloatingIP> getUnassignedFloatingIPsByNetworkUuid(final String networkUuid) { + if (networkUuid == null || networkUuid.isEmpty()) { + return null; + } + ArrayList<FloatingIP> availableFloatingIPs = Lists + .newArrayList(Iterables.filter(floatingIPApi.list().concat() + .toList(), new Predicate<FloatingIP>() { + @Override + public boolean apply(FloatingIP arg0) { + return arg0.getPortId() == null + && arg0.getFloatingNetworkId() != null + && arg0.getFloatingNetworkId().equals(networkUuid); + } + })); + return availableFloatingIPs; + } + + /** + * Get all unassigned Floating IPs. + * To get all unassigned Floating IPs from a floating network + * use {@link #getUnassignedFloatingIPsByNetworkUuid(String)} + * + * @return list of all the unassigned {@link FloatingIP} from all floating networks allocated to this tenant + */ + private ArrayList<FloatingIP> getUnassignedFloatingIPs() { + ArrayList<FloatingIP> availableFloatingIPs = Lists + .newArrayList(Iterables.filter(floatingIPApi.list().concat() + .toList(), new Predicate<FloatingIP>() { + @Override + public boolean apply(FloatingIP arg0) { + return arg0.getPortId() == null; + } + })); + return availableFloatingIPs; + } + + /** + * Get the {@link FloatingIP} by its Floating IP Address + * + * @param floatingIPAddress the Floating IP Address (a.k.a public IP address) + * @return the {@link FloatingIP} if found, null otherwise + */ + private FloatingIP getFloatingIPByIPAddress(final String floatingIPAddress) { + if (!isValidIP(floatingIPAddress)) { + return null; + } + + Iterable<FloatingIP> floatingIP = Iterables.filter(floatingIPApi.list().concat().toList(), + new Predicate<FloatingIP>() { + @Override + public boolean apply(FloatingIP input) { + return input.getFloatingIpAddress() != null + && input.getFloatingIpAddress().equals(floatingIPAddress); + } + }); + if (floatingIP.iterator().hasNext()) { + return floatingIP.iterator().next(); + } + return null; + } + + /** + * Get the {@link Port} by its fixed IP + * + * @param fixedIP the fixed IP of the port to be retrieved + * @return the {@link Port} if found, null otherwise + */ + private Port getPortByFixedIP(final String fixedIP) { + if (!isValidIP(fixedIP)) { + return null; + } + + Iterable<Port> port = Iterables.filter(portApi.list().concat().toList(), + new Predicate<Port>() { + @Override + public boolean apply(Port input) { + for (IP ip : input.getFixedIps()) { + if (ip.getIpAddress() != null + && ip.getIpAddress().equals(fixedIP)) { + return true; + } + } + return false; + } + }); + + // a fixed/private IP can be associated with at most one port + if (port.iterator().hasNext()) { + return port.iterator().next(); + } + return null; + } + + private void buildNeutronApi() { + + String iaasProviderNullMsg = "IaasProvider is null. Unable to build neutron API"; + assertNotNull(iaasProvider, iaasProviderNullMsg); + + String region = ComputeServiceBuilderUtil.extractRegion(iaasProvider); + String regionNullOrEmptyErrorMsg = String.format("Region is not set. Unable to build neutron API for the iaas provider %s", + iaasProvider.getProvider()); + assertNotNullAndNotEmpty(region, regionNullOrEmptyErrorMsg); + + String endpoint = iaasProvider.getProperty(CloudControllerConstants.JCLOUDS_ENDPOINT); + String endpointNullOrEmptyErrorMsg = String.format("Endpoint is not set. Unable to build neutorn API for the iaas provider %s", + iaasProvider.getProvider()); + assertNotNullAndNotEmpty(endpoint, endpointNullOrEmptyErrorMsg); + + Iterable<Module> modules = ImmutableSet.<Module>of(new SLF4JLoggingModule()); + + try { + this.neutronApi = ContextBuilder.newBuilder(provider).credentials(iaasProvider.getIdentity(), + iaasProvider.getCredential()).endpoint(endpoint).modules(modules).buildApi(NeutronApi.class); + } catch (Exception e) { + String msg = String.format("Unable to build neutron API for [provider=%s, identity=%s, credential=%s, endpoint=%s]", + provider, iaasProvider.getIdentity(), iaasProvider.getCredential(), endpoint); + log.error(msg, e); + throw new CloudControllerException(msg, e); + } + + this.portApi = neutronApi.getPortApi(region); + String portApiNullOrEmptyErrorMessage = String.format("Unable to get port Api from neutron Api for region ", region); + assertNotNull(portApi, portApiNullOrEmptyErrorMessage); + + this.floatingIPApi = neutronApi.getFloatingIPApi(region).get(); + String floatingIPApiNullOrEmptyErrorMessage = String.format("Unable to get floatingIP Api from neutron Api for region ", region); + assertNotNull(floatingIPApi, floatingIPApiNullOrEmptyErrorMessage); + } + + /** + * Check whether the given Object is null + * and throw {@link CloudControllerException} if it is null + * + * @param object the object to be null checked + * @param errorMessage the error message to logged and thrown + */ + private void assertNotNull(Object object, String errorMessage) { + if (null == object) { + log.error(errorMessage); + throw new CloudControllerException(errorMessage); + } + } + + /** + * Check whether the given String is null or empty + * and throw {@link CloudControllerException} if it is null or empty + * + * @param string the String to be null/empty checked + * @param errorMessage the error message to logged and thrown + */ + private void assertNotNullAndNotEmpty(String string, String errorMessage) { + if (null == string || string.isEmpty()) { + log.error(errorMessage); + throw new CloudControllerException(errorMessage); + } + } + + /** + * Validate the given IP address + * + * @param ip the IP to be validated + * @param errorMessage the error message to be logged and thrown + */ + private void assertValidIP(String ip, String errorMessage) { + if (!isValidIP(ip)) { + log.error(errorMessage); + throw new CloudControllerException(errorMessage); + } + } + + /** + * Check whether the given IP is valid. + * + * @param ip IP to be validated + * @return true if valid, false otherwise + */ + private boolean isValidIP(String ip) { + return (ip != null && InetAddresses.isInetAddress(ip)); + } + + /** + * Get networkUuid to list of {@link FloatingNetworks} map. + * This map will exclude those entries which are not having networkUuid or {@link FloatingNetworks} + * + * @param networkInterfaces array of {@link NetworkInterface} + * @return networkUuid to list of {@link FloatingNetworks} map + */ + public Map<String, List<FloatingNetwork>> getNetworkUuidToFloatingNetworksMap(NetworkInterface[] networkInterfaces) { + String nwInterfacesNullMsg = "Input NetworkInterface array cannot be null"; + assertNotNull(networkInterfaces, nwInterfacesNullMsg); + + Map<String, List<FloatingNetwork>> networkInterfaceToFloatingNetworksMap = + new HashMap<String, List<FloatingNetwork>>(); + for (NetworkInterface networkInterface : networkInterfaces) { + // if no floating networks defined, skip the network interface from the map + // because we don't need to care about this network interface when assigning floating IPs + if (networkInterface.getFloatingNetworks() == null + || networkInterface.getFloatingNetworks().getFloatingNetworks() == null + || networkInterface.getFloatingNetworks().getFloatingNetworks().length == 0) { + continue; + } + // if no network uuid is defined for a network interface, skip that interface from this map + if (networkInterface.getNetworkUuid() == null || networkInterface.getNetworkUuid().isEmpty()) { + continue; + } + networkInterfaceToFloatingNetworksMap.put(networkInterface.getNetworkUuid(), + Arrays.asList(networkInterface.getFloatingNetworks().getFloatingNetworks())); + } + return networkInterfaceToFloatingNetworksMap; + } + + /** + * Get fixedIp to list of {@link FloatingNetworks} map. + * This map will exclude those entries which are not having fixedIp or {@link FloatingNetworks} + * + * @param networkInterfaces array of {@link NetworkInterface} + * @return fixedIp to list of {@link FloatingNetworks} map + */ + public Map<String, List<FloatingNetwork>> getFixedIPToFloatingNetworksMap(NetworkInterface[] networkInterfaces) { + String nwInterfacesNullMsg = "Input NetworkInterface array cannot be null"; + assertNotNull(networkInterfaces, nwInterfacesNullMsg); + + Map<String, List<FloatingNetwork>> fixedIPToFloatingNetworksMap = + new HashMap<String, List<FloatingNetwork>>(); + for (NetworkInterface networkInterface : networkInterfaces) { + // if no floating networks defined, skip the network interface from the map + // because we don't need to care about this network interface when assigning floating IPs + if (networkInterface.getFloatingNetworks() == null + || networkInterface.getFloatingNetworks().getFloatingNetworks() == null + || networkInterface.getFloatingNetworks().getFloatingNetworks().length == 0) { + continue; + } + // if no fixed IP is defined for a network interface, skip that interface from this map + if (networkInterface.getFixedIp() == null || networkInterface.getFixedIp().isEmpty()) { + continue; + } + fixedIPToFloatingNetworksMap.put(networkInterface.getFixedIp(), + Arrays.asList(networkInterface.getFloatingNetworks().getFloatingNetworks())); + } + return fixedIPToFloatingNetworksMap; + } + + /** + * Get all predefined all floating IPs defined in cartridge definition. + * + * @param array of {@link NetworkInterface} + * @return list of predefined floating IPs + */ + public List<String> getAllPredefinedFloatingIPs(NetworkInterface[] networkInterfaces) { + String nwInterfacesNullMsg = "Input NetworkInterface array cannot be null"; + assertNotNull(networkInterfaces, nwInterfacesNullMsg); + + List<String> allPredefinedFloatingIPs = new ArrayList<String>(); + for (NetworkInterface networkInterface : networkInterfaces) { + // if no floating networks defined, skip it + if (null == networkInterface.getFloatingNetworks()) { + continue; + } + FloatingNetwork[] floatingNetworks = networkInterface.getFloatingNetworks().getFloatingNetworks(); + if (floatingNetworks == null || floatingNetworks.length == 0) { + continue; + } + + for (FloatingNetwork floatingNetwork : floatingNetworks) { + String floatingIP = floatingNetwork.getFloatingIP(); + // we are giving more priority to network uuid over fixed floating IPs + // so if both network uuid and floating IPs defined, we are not going to assign those floating IPs + // so these can be assigned to some other interfaces + // hence excluding from predefined floating IPs list + String networkUuid = floatingNetwork.getNetworkUuid(); + if (networkUuid == null || networkUuid.isEmpty()) { + if (floatingIP != null && InetAddresses.isInetAddress(floatingIP)) { + allPredefinedFloatingIPs.add(floatingIP); + } + } + } + } + return allPredefinedFloatingIPs; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/stratos/blob/efd079d0/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NovaNetworkingApi.java ---------------------------------------------------------------------- diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NovaNetworkingApi.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NovaNetworkingApi.java new file mode 100644 index 0000000..683c0a3 --- /dev/null +++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/NovaNetworkingApi.java @@ -0,0 +1,257 @@ +/* + * 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.stratos.cloud.controller.iaases.openstack.networking; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.stratos.cloud.controller.exception.CloudControllerException; +import org.apache.stratos.cloud.controller.util.ComputeServiceBuilderUtil; +import org.apache.stratos.cloud.controller.domain.IaasProvider; +import org.apache.stratos.cloud.controller.util.CloudControllerConstants; +import org.apache.stratos.cloud.controller.util.CloudControllerUtil; +import org.jclouds.compute.ComputeServiceContext; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.openstack.nova.v2_0.NovaApi; +import org.jclouds.openstack.nova.v2_0.domain.FloatingIP; +import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi; + +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * Leveraging openstack-nova networking apis. + * + * @author rajkumar + */ +public class NovaNetworkingApi implements OpenstackNetworkingApi { + + private static final Log log = LogFactory.getLog(NovaNetworkingApi.class); + private IaasProvider iaasProvider; + + public NovaNetworkingApi(IaasProvider iaasProvider) { + this.iaasProvider = iaasProvider; + } + + @Override + public List<String> associateAddresses(NodeMetadata node) { + + ComputeServiceContext context = iaasProvider.getComputeService() + .getContext(); + + String region = ComputeServiceBuilderUtil.extractRegion(iaasProvider); + + NovaApi novaApi = context.unwrapApi(NovaApi.class); + FloatingIPApi floatingIp = novaApi.getFloatingIPExtensionForZone( + region).get(); + + String ip = null; + // first try to find an unassigned IP. + ArrayList<FloatingIP> unassignedIps = Lists.newArrayList(Iterables + .filter(floatingIp.list(), + new Predicate<FloatingIP>() { + + @Override + public boolean apply(FloatingIP arg0) { + return arg0.getInstanceId() == null; + } + + })); + + if (!unassignedIps.isEmpty()) { + // try to prevent multiple parallel launches from choosing the same + // ip. + Collections.shuffle(unassignedIps); + ip = Iterables.getLast(unassignedIps).getIp(); + } + + // if no unassigned IP is available, we'll try to allocate an IP. + if (ip == null || ip.isEmpty()) { + String defaultFloatingIpPool = iaasProvider.getProperty(CloudControllerConstants.DEFAULT_FLOATING_IP_POOL); + FloatingIP allocatedFloatingIP; + if ((defaultFloatingIpPool == null) || "".equals(defaultFloatingIpPool)) { + allocatedFloatingIP = floatingIp.create(); + } else { + allocatedFloatingIP = floatingIp.allocateFromPool(defaultFloatingIpPool); + } + if (allocatedFloatingIP == null) { + String msg = "Failed to allocate an IP address."; + log.error(msg); + throw new CloudControllerException(msg); + } + ip = allocatedFloatingIP.getIp(); + } + + // wait till the fixed IP address gets assigned - this is needed before + // we associate a public IP + while (node.getPrivateAddresses() == null) { + CloudControllerUtil.sleep(1000); + } + + if (node.getPublicAddresses() != null + && node.getPublicAddresses().iterator().hasNext()) { + log.info("A public IP (" + + node.getPublicAddresses().iterator().next() + + ") is already allocated to the instance [id] : " + + node.getId()); + return null; + } + + int retries = 0; + //TODO make 5 configurable + while (retries < 5 + && !associateIp(floatingIp, ip, node.getProviderId())) { + + // wait for 5s + CloudControllerUtil.sleep(5000); + retries++; + } + + log.info("Successfully associated an IP address " + ip + + " for node with id: " + node.getId()); + + List<String> allocatedIPAddresses = new ArrayList<String>(); + allocatedIPAddresses.add(ip); + + return allocatedIPAddresses; + } + + @Override + public String associatePredefinedAddress(NodeMetadata node, String ip) { + if(log.isDebugEnabled()) { + log.debug("OpenstackNovaIaas:associatePredefinedAddress:ip:" + ip); + } + + ComputeServiceContext context = iaasProvider.getComputeService() + .getContext(); + + String region = ComputeServiceBuilderUtil.extractRegion(iaasProvider); + + FloatingIPApi floatingIp = context.unwrapApi(NovaApi.class).getFloatingIPExtensionForZone( + region).get(); + + if(log.isDebugEnabled()) { + log.debug("OpenstackNovaIaas:associatePredefinedAddress:floatingip:" + floatingIp); + } + + // get the list of all unassigned IP. + ArrayList<FloatingIP> unassignedIps = Lists.newArrayList(Iterables + .filter(floatingIp.list(), + new Predicate<FloatingIP>() { + + @Override + public boolean apply(FloatingIP arg0) { + // FIXME is this the correct filter? + return arg0.getFixedIp() == null; + } + + })); + + boolean isAvailable = false; + for (FloatingIP fip : unassignedIps) { + if(log.isDebugEnabled()) { + log.debug("OpenstackNovaIaas:associatePredefinedAddress:iterating over available floatingip:" + fip); + } + if (ip.equals(fip.getIp())) { + if(log.isDebugEnabled()) { + log.debug(String.format("OpenstackNovaIaas:associatePredefinedAddress:floating ip in use:%s /ip:%s", fip, ip)); + } + isAvailable = true; + break; + } + } + + if (isAvailable) { + // assign ip + if(log.isDebugEnabled()) { + log.debug("OpenstackNovaIaas:associatePredefinedAddress:assign floating ip:" + ip); + } + // exercise same code as in associateAddress() + // wait till the fixed IP address gets assigned - this is needed before + // we associate a public IP + + while (node.getPrivateAddresses() == null) { + CloudControllerUtil.sleep(1000); + } + + int retries = 0; + while (retries < 5 + && !associateIp(floatingIp, ip, node.getProviderId())) { + + // wait for 5s + CloudControllerUtil.sleep(5000); + retries++; + } + + NodeMetadataBuilder.fromNodeMetadata(node) + .publicAddresses(ImmutableSet.of(ip)).build(); + + log.info("OpenstackNovaIaas:associatePredefinedAddress:Successfully associated an IP address " + ip + + " for node with id: " + node.getId()); + } else { + // unable to allocate predefined ip, + log.info("OpenstackNovaIaas:associatePredefinedAddress:Unable to allocate predefined ip:" + + " for node with id: " + node.getId()); + return null; + } + + + NodeMetadataBuilder.fromNodeMetadata(node) + .publicAddresses(ImmutableSet.of(ip)).build(); + + log.info("OpenstackNovaIaas:associatePredefinedAddress::Successfully associated an IP address " + ip + + " for node with id: " + node.getId()); + + return ip; + } + + @Override + public void releaseAddress(String ip) { + + ComputeServiceContext context = iaasProvider.getComputeService() + .getContext(); + + String region = ComputeServiceBuilderUtil.extractRegion(iaasProvider); + + NovaApi novaApi = context.unwrapApi(NovaApi.class); + FloatingIPApi floatingIPApi = novaApi.getFloatingIPExtensionForZone(region).get(); + + for (FloatingIP floatingIP : floatingIPApi.list()) { + if (floatingIP.getIp().equals(ip)) { + floatingIPApi.delete(floatingIP.getId()); + break; + } + } + } + + private boolean associateIp(FloatingIPApi api, String ip, String id) { + try { + api.addToServer(ip, id); + return true; + } catch (RuntimeException ex) { + return false; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/stratos/blob/efd079d0/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/OpenstackNetworkingApi.java ---------------------------------------------------------------------- diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/OpenstackNetworkingApi.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/OpenstackNetworkingApi.java new file mode 100644 index 0000000..6de58d1 --- /dev/null +++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/openstack/networking/OpenstackNetworkingApi.java @@ -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.apache.stratos.cloud.controller.iaases.openstack.networking; + +import java.util.List; + +import org.jclouds.compute.domain.NodeMetadata; + +/** + * Openstack can support Neutron Network or Nova Network, but not both at same time. + * Each have different ways of assigning and releasing Floating IPs. + * Networking operation should be implemented by implementing this interface + * for different openstack networks. + * + * @author rajkumar + */ +public interface OpenstackNetworkingApi { + /** + * Associate FloatingIPs to the given node. + * NetworkInterfaces, FloatingNetworks and fixed IPs details + * will be retrieved from cartridge deployment. + * + * @param node the node to be associated with FloatingIPs + * @return list of associated FloatinigIPs + */ + public abstract List<String> associateAddresses(NodeMetadata node); + + /** + * Associate the given FloatingIP to the given node. + * NetworkInterfaces, FloatingNetworks and fixed IPs details + * will be retrieved from cartridge deployment. + * + * @param node Node to be associated with FloatingIPs + * @return list of associated FloatinigIPs + */ + public abstract String associatePredefinedAddress(NodeMetadata node, String ip); + + /** + * Deallocate/delete the given FloatingIP. + * + * @param ip FloatingIP address to be released. + */ + public abstract void releaseAddress(String ip); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/stratos/blob/efd079d0/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/util/CloudControllerConstants.java ---------------------------------------------------------------------- diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/util/CloudControllerConstants.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/util/CloudControllerConstants.java index 8e5e4d5..9c606b9 100644 --- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/util/CloudControllerConstants.java +++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/util/CloudControllerConstants.java @@ -133,6 +133,7 @@ public final class CloudControllerConstants { public static final String TENANT_ID_PROPERTY = "tenant_id"; public static final String ALIAS_PROPERTY = "alias"; public static final String AUTO_ASSIGN_IP_PROPERTY = "autoAssignIp"; + public static final String JCLOUDS_ENDPOINT = "jclouds.endpoint"; public static final String CRON_PROPERTY = "cron"; public static final String AMQP_CONNECTION_URL_PROPERTY = "amqpConnectionUrl"; public static final String AMQP_INITIAL_CONTEXT_FACTORY_PROPERTY = "amqpInitialContextFactory";
