/*******************************************************************************
* Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
*
* 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 org.cloudifysource.esc.driver.provisioning.openstack;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.cloudifysource.domain.cloud.AgentComponent;
import org.cloudifysource.domain.cloud.Cloud;
import org.cloudifysource.domain.cloud.DeployerComponent;
import org.cloudifysource.domain.cloud.DiscoveryComponent;
import org.cloudifysource.domain.cloud.FileTransferModes;
import org.cloudifysource.domain.cloud.GridComponents;
import org.cloudifysource.domain.cloud.OrchestratorComponent;
import org.cloudifysource.domain.cloud.RestComponent;
import org.cloudifysource.domain.cloud.UsmComponent;
import org.cloudifysource.domain.cloud.WebuiComponent;
import org.cloudifysource.domain.cloud.compute.ComputeTemplate;
import org.cloudifysource.domain.cloud.compute.ComputeTemplateNetwork;
import org.cloudifysource.domain.cloud.network.CloudNetwork;
import org.cloudifysource.domain.cloud.network.ManagementNetwork;
import org.cloudifysource.domain.cloud.network.NetworkConfiguration;
import org.cloudifysource.domain.network.AccessRule;
import org.cloudifysource.domain.network.AccessRules;
import org.cloudifysource.domain.network.PortRange;
import org.cloudifysource.domain.network.PortRangeEntry;
import org.cloudifysource.domain.network.PortRangeFactory;
import org.cloudifysource.dsl.utils.ServiceUtils;
import org.cloudifysource.dsl.utils.ServiceUtils.FullServiceName;
import org.cloudifysource.esc.driver.provisioning.BaseProvisioningDriver;
import org.cloudifysource.esc.driver.provisioning.CloudProvisioningException;
import org.cloudifysource.esc.driver.provisioning.ComputeDriverConfiguration;
import org.cloudifysource.esc.driver.provisioning.MachineDetails;
import org.cloudifysource.esc.driver.provisioning.ManagementProvisioningContext;
import org.cloudifysource.esc.driver.provisioning.ProvisioningContext;
import org.cloudifysource.esc.driver.provisioning.context.ValidationContext;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.ComputeLimits;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Flavor;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.FloatingIp;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Network;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.NovaServer;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.NovaServerNetwork;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.NovaServerResquest;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Port;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Quota;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.RouteFixedIp;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Router;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.RouterExternalGatewayInfo;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.SecurityGroup;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.SecurityGroupRule;
import org.cloudifysource.esc.driver.provisioning.openstack.rest.Subnet;
import org.cloudifysource.esc.driver.provisioning.validation.ValidationMessageType;
import org.cloudifysource.esc.driver.provisioning.validation.ValidationResultType;
import org.openspaces.admin.application.Application;
import org.openspaces.admin.application.Applications;
import com.j_spaces.kernel.Environment;
/**
* Openstack Driver which creates security groups and networks.
*
* @author victor
* @since 2.7.0
*
*/
public class OpenStackCloudifyDriver extends BaseProvisioningDriver {
private static final int HTTP_AUTHENTIFICATION_ERROR = 401;
private static final String CLOUDS_FOLDER_PATH = Environment.getHomeDirectory() + "clouds";
private static final String FILE_SEPARATOR = System.getProperty("file.separator");
private static final String MANAGEMENT_PUBLIC_ROUTER_NAME = "management-public-router";
private static final String DEFAULT_PROTOCOL = "tcp";
private static final String OPENSTACK_COMPUTE_ZONE = "openstack.compute.zone";
private static final int UNLIMITED_RESOURCE_QUOTA = -1;
private static final int DEFAULT_SECURITY_GROUPS_COUNT = 3;
private static final int DEFAULT_SECURITY_GROUP_RULES = 20;
private static final int DEFAULT_ROUTERS_COUNT = 1;
private static final int DEFAULT_MGMT_NETWORKS_COUNT = 1;
private static final int MANAGEMENT_SHUTDOWN_TIMEOUT = 60; // 60 seconds
private static final int CLOUD_NODE_STATE_POLLING_INTERVAL = 2000;
/**
* Key to set keyPairName. <br />
* For instance: <code>keyPairName="cloudify</code>"
*/
public static final String OPT_KEY_PAIR = "keyPairName";
/**
* Key to set endpoint. <br />
* For instance: <code>openstack.endpoint="https://<IP>:5000/v2.0/"</code>
* */
public static final String OPENSTACK_ENDPOINT = "openstack.endpoint";
/**
* Set the name to search to find openstack compute endpoint (default="nova"). <br />
* For instance: <code>computeServiceName="nova"</code>
*/
public static final String OPT_COMPUTE_SERVICE_NAME = "computeServiceName";
/**
* Set the name to search to find openstack networking endpoint (default="neutron"). <br />
* For instance: <code>networkServiceName="quantum"</code>
*/
public static final String OPT_NETWORK_SERVICE_NAME = "networkServiceName";
/**
* Set the network api version (default="v2.0"). <br />
* The Openstack network api need version in the URL (i.e.: https://192.168.2.100:9696/<b>v2.0</b>/networks). So we
* might need to provide the version number to the cloud driver. <br />
* For instance: <code>networkApiVersion="v2.0"</code>
* */
public static final String OPT_NETWORK_API_VERSION = "networkApiVersion";
/**
* Specify if you want the driver to handle the external networking (default="false").<br />
* By default, the driver will create a router and link it to an external network. If this property is set to
* <code>false</code> the driver will ignore this step.
*/
public static final String OPT_SKIP_EXTERNAL_NETWORKING = "skipExternalNetworking";
/**
* Use an existing external router.
* */
public static final String OPT_EXTERNAL_ROUTER_NAME = "externalRouterName";
/**
* Specify an external network to use. If no name is configured, the driver will pick the first external network it
* will find.<br />
* If you specify <code>externalRouterName</code>, this property is ignored.
* */
public static final String OPT_EXTERNAL_NETWORK_NAME = "externalNetworkName";
private OpenStackComputeClient computeApi;
private OpenStackNetworkClient networkApi;
private OpenStackNetworkConfigurationHelper networkHelper;
private OpenStackResourcePrefixes openstackPrefixes;
private String applicationName;
public static String getDefaultMangementPrefix() {
return MANAGMENT_MACHINE_PREFIX;
}
private static ResourceBundle defaultProvisioningDriverMessageBundle = ResourceBundle.getBundle(
"DefaultProvisioningDriverMessages", Locale.getDefault());
void setComputeApi(final OpenStackComputeClient computeApi) {
this.computeApi = computeApi;
}
void setNetworkApi(final OpenStackNetworkClient networkApi) {
this.networkApi = networkApi;
}
@Override
public void setConfig(final ComputeDriverConfiguration configuration) throws CloudProvisioningException {
this.networkHelper = new OpenStackNetworkConfigurationHelper(configuration);
super.setConfig(configuration);
String serviceName = null;
if (!this.management) {
final FullServiceName fsn = ServiceUtils.getFullServiceName(configuration.getServiceName());
applicationName = fsn.getApplicationName();
serviceName = fsn.getServiceName();
}
String managementGroup = cloud.getProvider().getManagementGroup();
managementGroup = managementGroup == null ? MANAGMENT_MACHINE_PREFIX : managementGroup;
this.openstackPrefixes = new OpenStackResourcePrefixes(managementGroup, applicationName, serviceName);
}
@Override
protected String getAvailabilityZone(final ComputeTemplate template) throws IllegalArgumentException {
String zone = null;
Map<String, Object> customSettings = template.getCustom();
if (customSettings != null) {
Object zoneObj = customSettings.get(OPENSTACK_COMPUTE_ZONE);
if (zoneObj != null) {
if (zoneObj instanceof String) {
zone = (String) zoneObj;
} else {
throw new IllegalArgumentException("Custom property " + OPENSTACK_COMPUTE_ZONE
+ " must be of type String");
}
}
}
if (StringUtils.isBlank(zone)) {
zone = super.getAvailabilityZone(template);
}
return zone;
}
private void initManagementSecurityGroups() throws CloudProvisioningException {
try {
final GridComponents components = this.cloud.getConfiguration().getComponents();
// default 7002
final AgentComponent agent = components.getAgent();
// default 7000 and 6666
final DeployerComponent deployer = components.getDeployer();
// default: 7001
final DiscoveryComponent discovery = components.getDiscovery();
// default: 7003
final OrchestratorComponent orchestrator = components.getOrchestrator();
// default: 7010-7110
final UsmComponent usm = components.getUsm();
// ** Clean security groups
this.cleanAllSecurityGroups();
// ** Create Cluster security group
final SecurityGroup clusterSecgroup = this.createSecurityGroup(this.openstackPrefixes.getClusterName());
// ** Create Management security group
final String managementSecgroupName = this.openstackPrefixes.getManagementName();
final SecurityGroup managementSecurityGroup = this.createSecurityGroup(managementSecgroupName);
// ** Create Agent security groups
final String agentSecgroupName = this.openstackPrefixes.getAgentName();
final SecurityGroup agentSecurityGroup = this.createSecurityGroup(agentSecgroupName);
// ** Create Management rules
@SuppressWarnings("unchecked")
final Set<Object> managementPorts = new HashSet<Object>(Arrays.asList(
agent.getPort(),
deployer.getPort(),
deployer.getWebsterPort(),
discovery.getPort(),
discovery.getDiscoveryPort(),
orchestrator.getPort(),
usm.getPortRange()
));
final String managementPortRange = StringUtils.join(managementPorts, ",");
this.createManagementRule(managementSecurityGroup.getId(), managementPortRange, clusterSecgroup.getId());
// ** Create Agent rules
@SuppressWarnings("unchecked")
final Set<Object> agentPorts = new HashSet<Object>(Arrays.asList(
agent.getPort(),
deployer.getPort(),
discovery.getPort(),
usm.getPortRange()
));
final String agentPortRange = StringUtils.join(agentPorts, ",");
this.createManagementRule(agentSecurityGroup.getId(), agentPortRange, clusterSecgroup.getId());
// ** Add Management public rules
final WebuiComponent webui = components.getWebui();
final RestComponent rest = components.getRest();
// Retrieve file transfert port
final String managementMachineTemplate = this.cloud.getConfiguration().getManagementMachineTemplate();
final ComputeTemplate template = this.cloud.getCloudCompute().getTemplates().get(managementMachineTemplate);
final FileTransferModes fileTransfer = template.getFileTransfer();
final List<?> publicPorts = Arrays.asList(fileTransfer.getDefaultPort(), webui.getPort(), rest.getPort());
final String publicPortRange = StringUtils.join(publicPorts, ",");
this.createManagementRule(managementSecurityGroup.getId(), publicPortRange, null);
} catch (final Exception e) {
try {
this.cleanAllSecurityGroups();
} catch (OpenstackException e1) {
logger.warning("Couldn't clean all security groups: " + e1.getMessage());
}
throw new CloudProvisioningException(e);
}
}
@Override
public Object getComputeContext() {
return this.computeApi;
}
private void cleanAllSecurityGroups() throws OpenstackException {
final String prefix = this.openstackPrefixes.getPrefix();
final List<SecurityGroup> securityGroupsByName = this.networkApi.getSecurityGroupsByPrefix(prefix);
for (final SecurityGroup securityGroup : securityGroupsByName) {
this.networkApi.deleteSecurityGroup(securityGroup.getId());
}
}
private void createManagementRule(final String targetSecgroupId, final String portRangeString,
final String remoteGroupId) throws OpenstackException {
final PortRange portRange = PortRangeFactory.createPortRange(portRangeString);
SecurityGroupRule request;
for (final PortRangeEntry entry : portRange.getRanges()) {
request = new SecurityGroupRule();
request.setSecurityGroupId(targetSecgroupId);
request.setDirection("ingress");
request.setProtocol(DEFAULT_PROTOCOL);
request.setPortRangeMax(entry.getTo() == null ? entry.getFrom().toString() : entry.getTo().toString());
request.setPortRangeMin(entry.getFrom().toString());
if (remoteGroupId == null) {
request.setRemoteIpPrefix("0.0.0.0/0");
} else {
request.setRemoteGroupId(remoteGroupId);
}
networkApi.createSecurityGroupRule(request);
}
}
private SecurityGroup createSecurityGroup(final String secgroupName) throws OpenstackException {
final SecurityGroup request = new SecurityGroup();
// Using '.' in security group name may cause security issues. see CLOUDIFY-2508.
final String validSecGroupName = secgroupName.replace('.', '-');
request.setName(validSecGroupName);
request.setDescription("Security groups " + validSecGroupName);
return networkApi.createSecurityGroupsIfNotExist(request);
}
@Override
protected void initDeployer(final Cloud cloud) {
final ComputeTemplate cloudTemplate;
if (this.management) {
final String managementMachineTemplate = cloud.getConfiguration().getManagementMachineTemplate();
cloudTemplate = cloud.getCloudCompute().getTemplates().get(managementMachineTemplate);
if (cloudTemplate == null) {
throw new IllegalStateException("Management compute template with name <"
+ managementMachineTemplate + "> could not be found.");
}
} else {
cloudTemplate = cloud.getCloudCompute().getTemplates().get(cloudTemplateName);
if (cloudTemplate == null) {
throw new IllegalStateException("Template with name <"
+ cloudTemplateName + "> could not be found.");
}
}
String endpoint = null;
final Map<String, Object> overrides = cloudTemplate.getOverrides();
if (overrides != null && !overrides.isEmpty()) {
endpoint = (String) overrides.get(OPENSTACK_ENDPOINT);
}
final String networkApiVersion = (String) cloudTemplate.getOptions().get(OPT_NETWORK_API_VERSION);
final String networkServiceName = (String) cloudTemplate.getOptions().get(OPT_NETWORK_SERVICE_NAME);
final String computeServiceName = (String) cloudTemplate.getOptions().get(OPT_COMPUTE_SERVICE_NAME);
final String cloudImageId = cloudTemplate.getImageId();
final String region = cloudImageId.split("/")[0];
final String cloudUser = cloud.getUser().getUser();
final String password = cloud.getUser().getApiKey();
if (cloudUser == null || password == null) {
throw new IllegalStateException("Cloud user or password not found");
}
final StringTokenizer st = new StringTokenizer(cloudUser, ":");
final String tenant = st.hasMoreElements() ? (String) st.nextToken() : null;
final String username = st.hasMoreElements() ? (String) st.nextToken() : null;
try {
this.computeApi = new OpenStackComputeClient(endpoint, username, password, tenant, region,
computeServiceName);
this.networkApi = new OpenStackNetworkClient(endpoint, username, password, tenant, region,
networkServiceName, networkApiVersion);
} catch (OpenstackJsonSerializationException e) {
throw new IllegalStateException(e);
}
}
@Override
public MachineDetails startMachine(final ProvisioningContext context, final long duration, final TimeUnit unit)
throws TimeoutException, CloudProvisioningException {
logger.fine(this.getClass().getName() + ": startMachine, management mode: " + management);
final long end = System.currentTimeMillis() + unit.toMillis(duration);
if (System.currentTimeMillis() > end) {
throw new TimeoutException("Starting a new machine timed out");
}
try {
// Create application secgroups
this.createSecurityGroup(this.openstackPrefixes.getApplicationName());
this.createSecurityGroup(this.openstackPrefixes.getServiceName());
this.createSecurityGroupsRules();
if (networkHelper.useApplicationNetworkTemplate()) {
// Network
final Network network = this.getOrCreateNetwork(this.networkHelper.getApplicationNetworkPrefixedName());
if (network != null) {
// Subnets
final NetworkConfiguration networkTemplate = this.networkHelper.getApplicationNetworkTemplate();
final List<org.cloudifysource.domain.cloud.network.Subnet> subnets =
networkTemplate.getSubnets();
for (final org.cloudifysource.domain.cloud.network.Subnet subnetConfig : subnets) {
this.getOrCreateSubnet(subnetConfig, network);
}
}
}
final String groupName =
serverNamePrefix + this.configuration.getServiceName() + "-" + counter.incrementAndGet();
// Using '.' in machine name may cause DNS and security group issues. See CLOUDIFY-2508.
final String validGroupName = groupName.replace('.', '-');
logger.fine("Starting a new cloud server with group: " + validGroupName);
final ComputeTemplate computeTemplate =
this.cloud.getCloudCompute().getTemplates().get(this.cloudTemplateName);
final MachineDetails md = this.createServer(validGroupName, end, computeTemplate, context.getLocationId());
return md;
} catch (final OpenstackException e) {
throw new CloudProvisioningException("Failed to start cloud machine", e);
}
}
@Override
public MachineDetails[] startManagementMachines(final ManagementProvisioningContext context, final long duration,
final TimeUnit unit) throws TimeoutException, CloudProvisioningException {
if (duration < 0) {
throw new TimeoutException("Starting a new machine timed out");
}
final long endTime = System.currentTimeMillis() + unit.toMillis(duration);
logger.fine("DefaultCloudProvisioning: startMachine - management == " + management);
// first check if management already exists
final MachineDetails[] existingManagementServers = this.getExistingManagementServers();
if (existingManagementServers.length > 0) {
final String serverDescriptions =
this.createExistingServersDescription(this.serverNamePrefix, existingManagementServers);
throw new CloudProvisioningException("Found existing servers matching group "
+ this.serverNamePrefix + ": " + serverDescriptions);
}
// Create management secgroups and rules
this.initManagementSecurityGroups();
// Create management networks
if (networkHelper.useManagementNetwork()) {
this.createManagementNetworkAndSubnets();
}
// launch the management machines
publishEvent(EVENT_ATTEMPT_START_MGMT_VMS);
final int numberOfManagementMachines = this.cloud.getProvider().getNumberOfManagementMachines();
final MachineDetails[] createdMachines = this.doStartManagementMachines(endTime, numberOfManagementMachines);
publishEvent(EVENT_MGMT_VMS_STARTED);
return createdMachines;
}
private void createManagementNetworkAndSubnets() throws CloudProvisioningException {
try {
// Clear existing network
this.cleanAllNetworks();
// Network
final String managementNetworkPrefixedName = this.networkHelper.getManagementNetworkPrefixedName();
final Network network = this.getOrCreateNetwork(managementNetworkPrefixedName);
if (network == null) {
throw new CloudProvisioningException("Fail to create '" + managementNetworkPrefixedName + "' network");
}
// Subnets
final NetworkConfiguration networkTemplate = this.networkHelper.getManagementNetworkTemplate();
final List<Subnet> subnets = new ArrayList<Subnet>();
if (networkTemplate.getSubnets() != null) {
for (final org.cloudifysource.domain.cloud.network.Subnet subnetConfig : networkTemplate.getSubnets()) {
final Subnet subnet = this.getOrCreateSubnet(subnetConfig, network);
subnets.add(subnet);
}
}
if (!this.networkHelper.skipExternalNetworking()) {
this.createExternalNetworking(network, subnets.get(0));
}
} catch (final Exception e) {
try {
this.cleanAllNetworks();
} catch (OpenstackException e1) {
logger.warning("Couldn't clean all networks: " + e1.getMessage());
}
throw new CloudProvisioningException(e);
}
}
private void createExternalNetworking(final Network network, final Subnet subnet)
throws OpenstackException, CloudProvisioningException {
final Router router;
if (this.networkHelper.isCreateExternalRouter()) {
final String publicNetworkId;
if (this.networkHelper.isCreateExternalNetwork()) {
publicNetworkId = networkApi.getPublicNetworkId();
} else {
final Network extNetwork = networkApi.getNetworkByName(this.networkHelper.getExternalNetworkName());
if (extNetwork == null) {
throw new CloudProvisioningException("Couldn't find external network '"
+ this.networkHelper.getExternalNetworkName() + "'");
}
if (!BooleanUtils.toBoolean(extNetwork.getRouterExternal())) {
throw new CloudProvisioningException("The network '"
+ this.networkHelper.getExternalNetworkName() + "' is not an external network");
}
publicNetworkId = extNetwork.getId();
}
final Router request = new Router();
request.setName(this.openstackPrefixes.getPrefix() + MANAGEMENT_PUBLIC_ROUTER_NAME);
request.setAdminStateUp(true);
request.setExternalGatewayInfo(new RouterExternalGatewayInfo(publicNetworkId));
router = networkApi.createRouter(request);
} else {
router = networkApi.getRouterByName(this.networkHelper.getExternalRouterName());
if (router == null) {
throw new CloudProvisioningException("Couldn't find external router '"
+ this.networkHelper.getExternalRouterName() + "'");
}
}
if (subnet == null) {
throw new CloudProvisioningException("Cannot add router interface because the network '"
+ network.getName() + "' don't have any subnets");
}
// Add interface
networkApi.addRouterInterface(router.getId(), subnet.getId());
}
private Subnet getOrCreateSubnet(final org.cloudifysource.domain.cloud.network.Subnet subnetConfig,
final Network network) throws CloudProvisioningException, OpenstackException {
Subnet subnet = null;
if (subnetConfig == null) {
throw new CloudProvisioningException("The network '" + network.getName()
+ "' is missing subnet configuration.");
} else {
// Search for a subnet with the specified name
final List<Subnet> subnets = networkApi.getSubnetsByNetworkId(network.getId());
for (Subnet sn : subnets) {
if (sn.getName().equals(subnetConfig.getName())) {
subnet = sn;
break;
}
}
if (subnet == null) {
// If the subnet with the configuration name don't exists, create it.
final Subnet subnetRequest = this.createSubnetRequest(subnetConfig, network.getId());
subnet = networkApi.createSubnet(subnetRequest);
}
}
if (subnet == null) {
throw new CloudProvisioningException("Missing subnets for network '" + network.getName() + "'.");
}
return subnet;
}
private Network getOrCreateNetwork(final String networkName) throws OpenstackException, CloudProvisioningException {
Network network = networkApi.getNetworkByName(networkName);
if (network == null) {
final Network networkRequest = new Network();
networkRequest.setName(networkName);
networkRequest.setAdminStateUp(true);
network = networkApi.createNetworkIfNotExists(networkRequest);
}
return network;
}
private Subnet createSubnetRequest(final org.cloudifysource.domain.cloud.network.Subnet subnetConfig,
final String networkId) {
final Subnet subnetRequest = new Subnet();
subnetRequest.setNetworkId(networkId);
subnetRequest.setCidr(subnetConfig.getRange());
subnetRequest.setName(subnetConfig.getName());
subnetRequest.setEnableDhcp(true);
final Map<String, String> options = subnetConfig.getOptions();
if (options.containsKey("dnsNameServers")) {
final String dnsNameServers = options.get("dnsNameServers");
subnetRequest.addDnsNameservers(dnsNameServers);
}
if (options.containsKey("gateway")) {
final String gatewayStr = options.get("gateway");
if (StringUtils.isNotEmpty(gatewayStr) && !"null".equals(gatewayStr)) {
subnetRequest.setGatewayIp(gatewayStr);
subnetRequest.addHostRoute(gatewayStr, "0.0.0.0/0"); // FIXME is it necessary ?
} else {
subnetRequest.setGatewayIp("null");
}
}
subnetRequest.setIpVersion("4");
return subnetRequest;
}
private void cleanAllNetworks() throws OpenstackException {
// Clean external router
Router router = null;
if (!this.networkHelper.skipExternalNetworking()) {
if (this.networkHelper.isCreateExternalRouter()) {
// The driver has created an external router
router = networkApi.getRouterByName(this.openstackPrefixes.getPrefix()
+ MANAGEMENT_PUBLIC_ROUTER_NAME);
} else {
// User has specified an external router to use
router = networkApi.getRouterByName(this.networkHelper.getExternalRouterName());
}
if (router != null) {
try {
final String privateIpNetworkName = this.networkHelper.getPrivateIpNetworkName();
final Network privateNetwork = this.networkApi.getNetworkByName(privateIpNetworkName);
if (privateNetwork != null) {
final String[] privateNetSubnetIds = privateNetwork.getSubnets();
if (privateNetSubnetIds != null && privateNetSubnetIds.length > 0) {
final List<Port> ports = networkApi.getPortsByDeviceId(router.getId());
if (ports != null) {
for (final Port port : ports) {
for (final RouteFixedIp fixedIp : port.getFixedIps()) {
for (final String id : privateNetSubnetIds) {
if (id.equals(fixedIp.getSubnetId())) {
networkApi.deleteRouterInterface(router.getId(), fixedIp.getSubnetId());
}
}
}
}
}
}
}
} catch (final Exception e) {
// If the private network doesn't exist there is no consequences:
// we can't detached a network which doesn't exist anymore.
logger.log(Level.WARNING, "Could not remove an interface from external router", e);
}
if (this.networkHelper.isCreateExternalRouter()) {
networkApi.deleteRouter(router.getId());
}
}
}
// Delete all remaining application networks
final List<Network> appNetworks = networkApi.getNetworkByPrefix(this.openstackPrefixes.getPrefix());
if (appNetworks != null) {
for (final Network network : appNetworks) {
if (router != null) {
final String[] privateNetSubnetIds = network.getSubnets();
if (privateNetSubnetIds != null && privateNetSubnetIds.length > 0) {
final List<Port> ports = networkApi.getPortsByDeviceId(router.getId());
if (ports != null) {
for (final Port port : ports) {
for (final RouteFixedIp fixedIp : port.getFixedIps()) {
for (final String id : privateNetSubnetIds) {
if (id.equals(fixedIp.getSubnetId())) {
networkApi.deleteRouterInterface(router.getId(), fixedIp.getSubnetId());
}
}
}
}
}
}
}
networkApi.deleteNetwork(network.getId());
}
}
}
@Override
public MachineDetails[] getExistingManagementServers() throws CloudProvisioningException {
try {
final String mngTemplateName = this.cloud.getConfiguration().getManagementMachineTemplate();
final ComputeTemplate template = this.cloud.getCloudCompute().getTemplates().get(mngTemplateName);
final List<NovaServer> servers = computeApi.getServersByPrefix(this.serverNamePrefix);
final MachineDetails[] mds = new MachineDetails[servers.size()];
for (int i = 0; i < servers.size(); i++) {
mds[i] = this.createMachineDetails(template, servers.get(i));
}
return mds;
} catch (final Exception e) {
throw new CloudProvisioningException(e);
}
}
private List<String> getServerIdsByPrefix(final String prefix) throws CloudProvisioningException {
List<String> serverIds = new ArrayList<String>();
try {
final List<NovaServer> servers = computeApi.getServersByPrefix(prefix);
for (NovaServer server : servers) {
serverIds.add(server.getId());
}
} catch (final Exception e) {
throw new CloudProvisioningException(e);
}
return serverIds;
}
private String createExistingServersDescription(final String managementMachinePrefix,
final MachineDetails[] existingManagementServers) {
logger.info("Found existing servers matching the name: "
+ managementMachinePrefix);
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (final MachineDetails machineDetails : existingManagementServers) {
final String existingManagementServerDescription = createManagementServerDescription(machineDetails);
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append("[").append(existingManagementServerDescription).append("]");
}
final String serverDescriptions = sb.toString();
return serverDescriptions;
}
private String createManagementServerDescription(final MachineDetails machineDetails) {
final StringBuilder sb = new StringBuilder();
sb.append("Machine ID: ").append(machineDetails.getMachineId());
if (machineDetails.getPublicAddress() != null) {
sb.append(", Public IP: ").append(machineDetails.getPublicAddress());
}
if (machineDetails.getPrivateAddress() != null) {
sb.append(", Private IP: ").append(machineDetails.getPrivateAddress());
}
return sb.toString();
}
@Override
protected MachineDetails createServer(final String serverName, final long endTime, final ComputeTemplate template)
throws CloudProvisioningException, TimeoutException {
return createServer(serverName, endTime, template, null);
}
private MachineDetails createServer(final String serverName, final long endTime, final ComputeTemplate template,
final String locationId) throws CloudProvisioningException, TimeoutException {
final String imageId = template.getImageId().split("/")[1];
final String hardwareId = template.getHardwareId().split("/")[1];
final String keyName = (String) template.getOptions().get(OPT_KEY_PAIR);
String serverId = null;
final List<String> reservedPortIds = new ArrayList<String>();
try {
final NovaServerResquest request = new NovaServerResquest();
request.setName(serverName);
request.setKeyName(keyName);
request.setImageRef(imageId);
request.setFlavorRef(hardwareId);
// Set the availability zone
String zone = locationId;
logger.finest("locationId received from Cloudify adapter: " + locationId);
if (StringUtils.isBlank(zone)) {
zone = getAvailabilityZone(template);
logger.finest("using template availability zone: " + zone);
}
if (StringUtils.isNotBlank(zone)) {
logger.fine("setting new instance availability zone: " + zone);
request.setAvailabilityZone(zone);
}
// Add management network if exists
if (this.networkHelper.useManagementNetwork()) {
final String managementNetworkName = this.networkHelper.getManagementNetworkPrefixedName();
final Network managementNetwork = this.networkApi.getNetworkByName(managementNetworkName);
if (managementNetwork == null) {
throw new CloudProvisioningException("Unexpected missing management network '"
+ managementNetworkName + "'");
}
if (managementNetwork.getSubnets() == null || managementNetwork.getSubnets().length <= 0) {
throw new CloudProvisioningException("Unexpected missing subnet in management network '"
+ managementNetworkName + "'");
}
if (managementNetwork.getSubnets().length == 1) {
request.addNetworks(managementNetwork.getId());
} else {
final Port port = this.addPortToRequest(request,
managementNetwork.getId(), managementNetwork.getSubnets());
reservedPortIds.add(port.getId());
}
}
// Add compute networks
for (final String networkName : this.networkHelper.getComputeNetworks()) {
final Network network = this.networkApi.getNetworkByName(networkName);
if (network == null) {
throw new CloudProvisioningException("Couldn't find network '" + networkName + "'");
}
if (network.getSubnets() == null || network.getSubnets().length <= 0) {
throw new CloudProvisioningException("Unexpected missing subnet in network '" + networkName + "'");
}
if (network.getSubnets().length == 1) {
request.addNetworks(network.getId());
} else {
final Port port = this.addPortToRequest(request, network.getId(), network.getSubnets());
reservedPortIds.add(port.getId());
}
}
// Add template networks
if (!management && this.networkHelper.useApplicationNetworkTemplate()) {
final String prefixedAppliNetworkName = this.networkHelper.getApplicationNetworkPrefixedName();
final Network templateNetwork = this.networkApi.getNetworkByName(prefixedAppliNetworkName);
if (templateNetwork == null) {
throw new CloudProvisioningException("Unexpected missing management network '"
+ prefixedAppliNetworkName + "'");
}
if (templateNetwork.getSubnets() == null || templateNetwork.getSubnets().length <= 0) {
throw new CloudProvisioningException("Unexpected missing subnet in management network '"
+ prefixedAppliNetworkName + "'");
}
if (templateNetwork.getSubnets().length == 1) {
request.addNetworks(templateNetwork.getId());
} else {
final Port port = this.addPortToRequest(request,
templateNetwork.getId(), templateNetwork.getSubnets());
reservedPortIds.add(port.getId());
}
}
NovaServer newServer = computeApi.createServer(request);
serverId = newServer.getId();
newServer = this.waitForServerToBecomeReady(serverId, endTime);
// Add security groups to all ports
Object securityGroupsObj = template.getOptions().get("securityGroupNames");
if (securityGroupsObj == null) {
securityGroupsObj = template.getOptions().get("securityGroups");
}
final List<String> securityGroups = new ArrayList<String>();
if (securityGroupsObj != null) {
if (securityGroupsObj instanceof String[]) {
securityGroups.addAll(Arrays.asList(((String[]) securityGroupsObj)));
}
}
if (management) {
securityGroups.add(this.openstackPrefixes.getManagementName());
securityGroups.add(this.openstackPrefixes.getClusterName());
this.setSecurityGroupsToServer(serverId, securityGroups.toArray(new String[securityGroups.size()]));
} else {
securityGroups.add(this.openstackPrefixes.getAgentName());
securityGroups.add(this.openstackPrefixes.getClusterName());
securityGroups.add(this.openstackPrefixes.getApplicationName());
securityGroups.add(this.openstackPrefixes.getServiceName());
this.setSecurityGroupsToServer(serverId, securityGroups.toArray(new String[securityGroups.size()]));
}
// Associate floating ips if configured
if (this.networkHelper.associateFloatingIp()) {
final String privateIPNetworkName = this.networkHelper.getPrivateIpNetworkName();
final Network privateIpNetwork = this.networkApi.getNetworkByName(privateIPNetworkName);
if (privateIpNetwork == null) {
throw new CloudProvisioningException("Couldn't find network '" + privateIPNetworkName
+ "' to assign floating IP.");
}
networkApi.createAndAssociateFloatingIp(serverId, privateIpNetwork.getId());
}
final MachineDetails md = this.createMachineDetails(template, newServer);
return md;
} catch (final Exception e) {
logger.log(Level.SEVERE, "An error occured during initialization."
+ " Shutting down machine and cleaning openstack resources", e);
if (serverId != null) {
try {
computeApi.deleteServer(serverId);
} catch (final OpenstackException e1) {
logger.log(Level.WARNING, "Cleaning after error. Could not delete server.", e1);
}
} else {
for (final String portId : reservedPortIds) {
try {
// Application port are created before the VM.
// So it can happen that port is created but an error occurs on VM instantiation.
// In this case, we have to clear the port.
// * Note: Port is deleted with server deletion, so no need to handle port deletion once the
// server has been associated to the port.
networkApi.deletePort(portId);
} catch (final OpenstackException e1) {
logger.log(Level.WARNING, "Cleaning after error. Could not delete server.", e1);
}
}
}
throw new CloudProvisioningException(e);
}
}
/**
* Create a port to attached to the VM and add it to the request. <br />
*/
private Port addPortToRequest(final NovaServerResquest request, final String networkId, final String[] subnetIds)
throws OpenstackException {
final Port port = new Port();
for (final String subnetId : subnetIds) {
final RouteFixedIp fixedIp = new RouteFixedIp();
fixedIp.setSubnetId(subnetId);
port.addFixedIp(fixedIp);
}
port.setNetworkId(networkId);
final Port createdPort = this.networkApi.createPort(port);
final NovaServerNetwork nsn = new NovaServerNetwork();
nsn.setPort(createdPort.getId());
request.addNetworks(nsn);
return createdPort;
}
private void setSecurityGroupsToServer(final String serverId, final String... securityGroupNames)
throws OpenstackException, CloudProvisioningException {
final List<Port> ports = networkApi.getPortsByDeviceId(serverId);
for (final Port port : ports) {
final Port updateRequest = new Port();
updateRequest.setId(port.getId());
for (final String sgn : securityGroupNames) {
final SecurityGroup sg = networkApi.getSecurityGroupsByName(sgn);
if (sg == null) {
throw new CloudProvisioningException("Couldn't find security group '" + sgn + "'");
}
updateRequest.addSecurityGroup(sg.getId());
}
networkApi.updatePort(updateRequest);
}
}
private void createSecurityGroupsRules() throws OpenstackException, CloudProvisioningException {
final String serviceSecgroupName = this.openstackPrefixes.getServiceName();
final SecurityGroup serviceSecGroup = networkApi.getSecurityGroupsByName(serviceSecgroupName);
if (serviceSecGroup == null) {
throw new CloudProvisioningException("Couldn't find security group '" + serviceSecgroupName + "'");
}
final String managementSecgroupName = this.openstackPrefixes.getManagementName();
final SecurityGroup managementSecGroup = networkApi.getSecurityGroupsByName(managementSecgroupName);
if (managementSecGroup == null) {
throw new CloudProvisioningException("Couldn't find security group '" + managementSecgroupName + "'");
}
// Open the transfert mode port to the managers
final ComputeTemplate cloudTemplate = cloud.getCloudCompute().getTemplates().get(cloudTemplateName);
final String port = Integer.toString(cloudTemplate.getFileTransfer().getDefaultPort());
final SecurityGroupRule request = new SecurityGroupRule();
request.setSecurityGroupId(serviceSecGroup.getId());
request.setDirection("ingress");
request.setProtocol(DEFAULT_PROTOCOL);
request.setPortRangeMax(port);
request.setPortRangeMin(port);
request.setRemoteGroupId(managementSecGroup.getId());
networkApi.createSecurityGroupRule(request);
// Create service rules
final AccessRules accessRules = this.networkHelper.getServiceAccessRules();
if (accessRules != null) {
for (final AccessRule accessRule : accessRules.getIncoming()) {
this.createAccessRule(serviceSecGroup.getId(), "ingress", accessRule);
}
for (final AccessRule accessRule : accessRules.getOutgoing()) {
// If there is egress rules defined. we should delete the openstack default egress rules.
this.deleteEgressRulesFromSecurityGroup(this.openstackPrefixes.getServiceName());
this.createAccessRule(serviceSecGroup.getId(), "egress", accessRule);
}
}
}
private void deleteEgressRulesFromSecurityGroup(final String securityGroupName) throws OpenstackException {
final SecurityGroup securityGroup = networkApi.getSecurityGroupsByName(securityGroupName);
if (securityGroup != null) {
final SecurityGroupRule[] securityGroupRules = securityGroup.getSecurityGroupRules();
if (securityGroupRules != null) {
for (final SecurityGroupRule rule : securityGroupRules) {
if ("egress".equals(rule.getDirection())) {
networkApi.deleteSecurityGroupRule(rule.getId());
}
}
}
}
}
private void createAccessRule(final String serviceSecgroupId, final String direction, final AccessRule accessRule)
throws OpenstackException, CloudProvisioningException {
// Parse ports
final PortRange portRange = PortRangeFactory.createPortRange(accessRule.getPortRange());
if (portRange != null && !portRange.getRanges().isEmpty()) {
String targetSecurityGroupId = serviceSecgroupId;
String ip = "0.0.0.0/0";
String group = null;
switch (accessRule.getType()) {
case PUBLIC:
// Rules to apply to public network
break;
case SERVICE:
// Rules with group filtering
group = this.openstackPrefixes.getServiceName();
break;
case APPLICATION:
// Rules with group filtering
group = this.openstackPrefixes.getApplicationName();
break;
case CLUSTER:
// Rules with group filtering
group = this.openstackPrefixes.getClusterName();
break;
case GROUP:
// Rules with group filtering
group = accessRule.getTarget();
break;
case RANGE:
// Rules with ip filtering
if (accessRule.getTarget() == null) {
throw new CloudProvisioningException("No IP defined for the 'Range' access rule type :"
+ accessRule);
}
ip = accessRule.getTarget();
break;
case PRIVATE:
default:
throw new CloudProvisioningException("Unsupported type of rule '" + accessRule.getType() + "'");
}
SecurityGroup existingSecgroup = null;
if (group != null) {
existingSecgroup = this.networkApi.getSecurityGroupsByName(group);
if (existingSecgroup == null) {
throw new CloudProvisioningException("Security group '" + group + "' does not exist.");
}
}
// Create rules
for (final PortRangeEntry pre : portRange.getRanges()) {
final SecurityGroupRule request = new SecurityGroupRule();
request.setDirection(direction);
request.setProtocol(DEFAULT_PROTOCOL);
request.setSecurityGroupId(targetSecurityGroupId);
request.setPortRangeMax(pre.getTo() == null ? pre.getFrom().toString() : pre.getTo().toString());
request.setPortRangeMin(pre.getFrom().toString());
if (existingSecgroup != null) {
request.setRemoteGroupId(existingSecgroup.getId());
} else {
request.setRemoteIpPrefix(ip);
}
networkApi.createSecurityGroupRule(request);
}
}
}
private MachineDetails createMachineDetails(final ComputeTemplate template, final NovaServer server)
throws CloudProvisioningException {
try {
final MachineDetails md = this.createMachineDetailsForTemplate(template);
md.setMachineId(server.getId());
md.setCloudifyInstalled(false);
md.setInstallationDirectory(null);
md.setOpenFilesLimit(template.getOpenFilesLimit());
md.setLocationId(server.getAvailabilityZone());
// md.setInstallationDirectory(template.getRemoteDirectory());
// md.setRemoteDirectory(remoteDirectory);
// md.setInstallerConfigutation(installerConfigutation);
// md.setKeyFile(keyFile);
// md.setLocationId(locationId);
final String privateIpNetworkName = this.networkHelper.getPrivateIpNetworkName();
final Network privateIpNetwork = this.networkApi.getNetworkByName(privateIpNetworkName);
if (privateIpNetwork == null) {
throw new CloudProvisioningException("Couldn't find network '" + privateIpNetworkName
+ "' to set private IP.");
}
final Port privateIpPort = networkApi.getPort(server.getId(), privateIpNetwork.getId());
if (privateIpPort == null) {
throw new CloudProvisioningException("Server '" + server.getName()
+ "' has no port on network '" + privateIpNetwork.getName() + "'.");
}
if (privateIpPort.getFixedIps() == null || privateIpPort.getFixedIps().isEmpty()) {
throw new CloudProvisioningException("No fixed IP found on the port which link server '"
+ server.getName() + "' tonetwork '" + privateIpNetwork.getName() + "'.");
}
final RouteFixedIp fixedIp = privateIpPort.getFixedIps().get(0);
md.setPrivateAddress(fixedIp.getIpAddress());
if (this.networkHelper.associateFloatingIp()) {
final FloatingIp floatingIp = networkApi.getFloatingIpByPortId(privateIpPort.getId());
if (floatingIp != null) {
md.setPublicAddress(floatingIp.getFloatingIpAddress());
}
}
final String applicationNetworkName = this.networkHelper.getApplicationNetworkPrefixedName();
if (applicationNetworkName != null) {
// Since it is possible that the service itself will prefer to be available only on the application
// network and not on all networks, the cloud driver should add an environment variable specifying the
// IP of the NIC that is connected to the application network.
final Network appliNetwork = this.networkApi.getNetworkByName(applicationNetworkName);
final Port appliPort = networkApi.getPort(server.getId(), appliNetwork.getId());
final RouteFixedIp appliFixedIp = appliPort.getFixedIps().get(0);
Map<String, String> env = new HashMap<String, String>();
env.put("CLOUDIFY_APPLICATION_NETWORK_IP", appliFixedIp.getIpAddress());
md.setEnvironment(env);
}
this.handleServerCredentials(md, template);
return md;
} catch (Exception e) {
throw new CloudProvisioningException(e);
}
}
private NovaServer waitForServerToBecomeReady(final String serverId, final long endTime)
throws CloudProvisioningException, InterruptedException, TimeoutException {
while (System.currentTimeMillis() < endTime) {
final NovaServer server;
try {
server = computeApi.getServerDetails(serverId);
} catch (final OpenstackException e) {
throw new CloudProvisioningException(e);
}
if (server == null) {
logger.fine("Server Status (" + serverId + ") Not Found, please wait...");
Thread.sleep(CLOUD_NODE_STATE_POLLING_INTERVAL);
break;
} else {
switch (server.getStatus()) {
case ACTIVE:
return server;
case BUILD:
logger.fine("Server Status (" + serverId + ") still PENDING, please wait...");
Thread.sleep(CLOUD_NODE_STATE_POLLING_INTERVAL);
break;
default:
throw new CloudProvisioningException(
"Failed to allocate server - Cloud reported node in "
+ server.getStatus().toString()
+ " state. Node details: " + server);
}
}
}
throw new TimeoutException("Node failed to reach RUNNING mode in time");
}
@Override
protected void handleProvisioningFailure(final int numberOfManagementMachines, final int numberOfErrors,
final Exception firstCreationException, final MachineDetails[] createdManagementMachines)
throws CloudProvisioningException {
logger.severe("Of the required " + numberOfManagementMachines
+ " management machines, " + numberOfErrors
+ " failed to start.");
if (numberOfManagementMachines > numberOfErrors) {
logger.severe("Shutting down the other management machines");
for (final MachineDetails machineDetails : createdManagementMachines) {
if (machineDetails != null) {
logger.severe("Shutting down machine: " + machineDetails);
try {
this.computeApi.deleteServer(machineDetails.getMachineId());
} catch (final OpenstackException e) {
throw new CloudProvisioningException(e);
}
}
}
}
try {
this.cleanAllSecurityGroups();
} catch (final OpenstackException e) {
logger.warning(e.getMessage());
}
try {
this.cleanAllNetworks();
} catch (final OpenstackException e) {
logger.warning(e.getMessage());
}
throw new CloudProvisioningException(
"One or more management machines failed. The first encountered error was: "
+ firstCreationException.getMessage(), firstCreationException);
}
@Override
public void stopManagementMachines() throws TimeoutException, CloudProvisioningException {
try {
final MachineDetails[] managementServers = this.getExistingManagementServers();
if (managementServers.length == 0) {
throw new CloudProvisioningException(
"Could not find any management machines for this cloud (management machine prefix is: "
+ this.serverNamePrefix + ")");
}
for (final MachineDetails md : managementServers) {
try {
this.releaseFloatingIpsForServerId(md.getMachineId());
this.computeApi.deleteServer(md.getMachineId());
} catch (final Exception e) {
throw new CloudProvisioningException(e);
}
}
for (final MachineDetails md : managementServers) {
try {
this.waitForServerToBeShutdown(md.getMachineId(), MANAGEMENT_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// ** Clean security groups & networks
try {
this.cleanAllSecurityGroups();
this.cleanAllNetworks();
} catch (final Exception e) {
logger.warning("Couldn't clean security groups " + this.openstackPrefixes.getPrefix() + "*");
}
} finally {
if (this.computeApi != null) {
this.computeApi.close();
}
if (this.networkApi != null) {
this.networkApi.close();
}
}
}
@Override
public boolean stopMachine(final String serverIp, final long duration, final TimeUnit unit)
throws CloudProvisioningException, TimeoutException, InterruptedException {
boolean stopResult = false;
logger.info("Stop Machine - machineIp: " + serverIp);
logger.info("Looking up cloud server with IP: " + serverIp);
final NovaServer server;
try {
// We must provide the security group name.
// Indeed with network support, 2 VMs of different services can now have the same ip address.
// We must be sure to delete the right server.
server = computeApi.getServerByIpAndSecurityGroup(serverIp, this.openstackPrefixes.getServiceName());
} catch (final OpenstackException e) {
throw new CloudProvisioningException(e);
}
if (server != null) {
logger.info("Found server: " + server.getId() + ". Shutting it down and waiting for shutdown to complete");
// Release and delete floating Ip if exists
this.releaseFloatingIpsForServerId(server.getId());
// Delete server
try {
computeApi.deleteServer(server.getId());
} catch (final OpenstackException e) {
throw new CloudProvisioningException(e);
}
if (duration != 0) {
this.waitForServerToBeShutdown(server.getId(), duration, unit);
}
logger.info("Server: " + server.getId() + " is shutdown.");
stopResult = true;
} else {
logger.log(Level.SEVERE,
"Received scale in request for machine with ip "
+ serverIp
+ " but this IP could not be found in the Cloud server list");
stopResult = false;
}
return stopResult;
}
@Override
public void terminateAllResources(final long duration, final TimeUnit unit)
throws TimeoutException, CloudProvisioningException {
// TODO: throw CloudProvisioningException, don't ignore everything
logger.info("Attempting to terminate all cloud resources (timeout set to " + duration + " " + unit + ")");
final long endTime = System.currentTimeMillis() + unit.toMillis(duration);
try {
// terminating management machines
terminateMachinesByPrefix(cloud.getProvider().getManagementGroup(), endTime);
// terminating agent machines
terminateMachinesByPrefix(cloud.getProvider().getMachineNamePrefix(), endTime);
// terminating security groups
try {
this.cleanAllSecurityGroups();
} catch (final Exception e) {
logger.warning("Couldn't clean security groups " + this.openstackPrefixes.getPrefix() + "*");
logger.warning("Reported error: " + e.getMessage() + ", stack trace: " + e.getStackTrace());
}
// terminating networks
try {
this.cleanAllNetworks();
} catch (final Exception e) {
logger.warning("Couldn't clean networks " + this.openstackPrefixes.getPrefix() + "*");
logger.warning("Reported error: " + e.getMessage() + ", stack trace: " + e.getStackTrace());
}
} finally {
if (this.computeApi != null) {
this.computeApi.close();
}
if (this.networkApi != null) {
this.networkApi.close();
}
}
}
/**
* Terminates all machines with the given prefix. Does not stop on exceptions.
* @param prefix The machines prefix
* @param endTime timeout
*/
private void terminateMachinesByPrefix(final String prefix, final long endTime) {
try {
List<String> serverIds = getServerIdsByPrefix(prefix);
for (String serverId : serverIds) {
try {
this.releaseFloatingIpsForServerId(serverId);
this.computeApi.deleteServer(serverId);
this.waitForServerToBeShutdown(serverId, endTime);
} catch (final InterruptedException e) {
// TODO: wait was interrupted, log and continue
logger.warning("thread was interrupted while waiting for machine (" + serverId + ") to shutdown."
+ " continuing...");
} catch (final Exception e) {
logger.warning("Couldn't terminate machine " + serverId + ". Continuing to terminate resourcse."
+ " reported error: " + e.getMessage()
+ ", stack trace: " + e.getStackTrace());
}
}
} catch (CloudProvisioningException e) {
logger.warning("Failed to terminate servers with prefix: " + prefix + ", error while searching servers: "
+ e.getMessage() + ", stack trace: " + e.getStackTrace());
}
}
private void releaseFloatingIpsForServerId(final String serverId) {
try {
final List<Port> ports = networkApi.getPortsByDeviceId(serverId);
if (ports != null) {
for (final Port port : ports) {
final FloatingIp floatingIp = networkApi.getFloatingIpByPortId(port.getId());
if (floatingIp != null) {
try {
logger.info("Deleting Floating ip: " + floatingIp);
networkApi.deleteFloatingIP(floatingIp.getId());
} catch (final Exception e) {
logger.warning("Couldn't delete floating ip: " + floatingIp + " cause: " + e.getMessage());
}
}
}
}
} catch (final OpenstackException e) {
logger.log(Level.WARNING, "Could not release floating ip associated to server id='" + serverId + "'", e);
}
}
private void waitForServerToBeShutdown(final String serverId, final long duration, final TimeUnit unit)
throws CloudProvisioningException, InterruptedException, TimeoutException {
logger.finer("Wait server '" + serverId + "' to shutdown (" + duration + " " + unit + ")");
final long endTime = System.currentTimeMillis() + unit.toMillis(duration);
waitForServerToBeShutdown(serverId, endTime);
}
private void waitForServerToBeShutdown(final String serverId, final long endTime)
throws CloudProvisioningException, InterruptedException, TimeoutException {
logger.finest("Waiting for server '" + serverId + "' to shutdown");
while (System.currentTimeMillis() < endTime) {
final NovaServer server;
try {
server = computeApi.getServerDetails(serverId);
} catch (final OpenstackException e) {
throw new CloudProvisioningException(e);
}
if (server == null) {
logger.fine("Server Status (" + serverId + ") Not Found. Considered deleted.");
return;
} else {
switch (server.getStatus()) {
case STOPPED:
case DELETED:
return;
case ERROR:
case UNKNOWN:
case UNRECOGNIZED:
throw new CloudProvisioningException(
"Failed to allocate server - Cloud reported node in "
+ server.getStatus().toString()
+ " state. Node details: " + server);
default:
logger.fine("Server Status (" + serverId + ") is " + server.getStatus()
+ ", please wait until shutdown...");
Thread.sleep(CLOUD_NODE_STATE_POLLING_INTERVAL);
break;
}
}
}
throw new TimeoutException("Node failed to reach SHUTDOWN mode in time");
}
@Override
public void onServiceUninstalled(final long duration, final TimeUnit unit) throws InterruptedException,
TimeoutException, CloudProvisioningException {
final String ssgName = this.openstackPrefixes.getServiceName();
logger.info("Service '" + ssgName + "'is being uninstall.");
try {
final Applications applications = this.admin.getApplications();
final Application application = applications.getApplication(this.applicationName);
if (application == null) {
logger.info("No remaining services in the application.");
logger.info("Delete the application security group.");
final String applicationName = this.openstackPrefixes.getApplicationName();
final SecurityGroup secgroup = this.networkApi.getSecurityGroupsByName(applicationName);
if (secgroup != null) {
networkApi.deleteSecurityGroup(secgroup.getId());
}
if (this.networkHelper.useApplicationNetworkTemplate()) {
logger.info("Delete the network.");
final String prefixedNetworkName = this.networkHelper.getApplicationNetworkPrefixedName();
try {
final Network appliNetwork = networkApi.getNetworkByName(prefixedNetworkName);
networkApi.deleteNetwork(appliNetwork.getId());
} catch (final Exception e) {
logger.warning("Network '" + prefixedNetworkName + "' was not deleted: " + e.getMessage());
}
}
}
logger.info("Clean service's security group :" + ssgName + "*");
final List<SecurityGroup> securityGroups = this.networkApi.getSecurityGroupsByPrefix(ssgName);
for (final SecurityGroup securityGroup : securityGroups) {
this.networkApi.deleteSecurityGroup(securityGroup.getId());
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Fail to clean security group resources of service " + ssgName, e);
} finally {
if (this.computeApi != null) {
this.computeApi.close();
}
if (this.networkApi != null) {
this.networkApi.close();
}
}
}
@Override
public void onMachineFailure(final ProvisioningContext context, final long duration, final TimeUnit unit)
throws CloudProvisioningException, TimeoutException {
logger.finest("Handling compute resources following machine failure");
// customary call to the super implementation
super.onMachineFailure(context, duration, unit);
MachineDetails previoudMachineDetails = context.getPreviousMachineDetails();
logger.info("Releasing floating IPs following failure of machine: " + previoudMachineDetails.getMachineId());
releaseFloatingIpsForServerId(context.getPreviousMachineDetails().getMachineId());
}
/**
* returns the message as it appears in the DefaultProvisioningDriver message bundle.
*
* @param msgName
* the message key as it is defined in the message bundle.
* @param arguments
* the message arguments
* @return the formatted message according to the message key.
*/
protected String getFormattedMessage(final String msgName, final Object... arguments) {
return getFormattedMessage(getDefaultProvisioningDriverMessageBundle(), msgName, arguments);
}
/**
* Returns the message bundle of this cloud driver.
*
* @return the message bundle of this cloud driver.
*/
protected static ResourceBundle getDefaultProvisioningDriverMessageBundle() {
if (defaultProvisioningDriverMessageBundle == null) {
defaultProvisioningDriverMessageBundle = ResourceBundle.getBundle("DefaultProvisioningDriverMessages",
Locale.getDefault());
}
return defaultProvisioningDriverMessageBundle;
}
@Override
public void validateCloudConfiguration(final ValidationContext validationContext)
throws CloudProvisioningException {
String cloudFolder = CLOUDS_FOLDER_PATH + FILE_SEPARATOR + cloud.getName();
String groovyFile = cloudFolder + FILE_SEPARATOR + cloud.getName() + "-cloud.groovy";
String propertiesFile = cloudFolder + FILE_SEPARATOR + cloud.getName() + "-cloud.properties";
validationContext.validationEvent(ValidationMessageType.TOP_LEVEL_VALIDATION_MESSAGE,
getFormattedMessage("validating_all_templates"));
final Map<String, ComputeTemplate> templates = cloud.getCloudCompute().getTemplates();
final String mangementTemplateName = cloud.getConfiguration().getManagementMachineTemplate();
final ComputeTemplate managementComputeTemplate =
cloud.getCloudCompute().getTemplates().get(mangementTemplateName);
// validating openstack endpoint
this.validateOpenstackEndpoint(validationContext, managementComputeTemplate);
// validating credentials
validateCredentials(validationContext);
// validate compute quotas
validateComputeQuotas(validationContext, managementComputeTemplate);
// validate network quotas
validateNetworkQuotas(validationContext);
// validating management network/subnets configuration
final CloudNetwork cloudNetwork = configuration.getCloud().getCloudNetwork();
this.validateManagementNetwork(validationContext, managementComputeTemplate, cloudNetwork);
// validating templates networks configuration
if (cloudNetwork != null) {
this.validateTemplateNetworks(validationContext, cloudNetwork, templates.values());
}
// validating templates
this.validateComputeTemplates(validationContext, groovyFile, propertiesFile, templates);
}
private void validateComputeQuotas(final ValidationContext validationContext,
final ComputeTemplate managementComputeTemplate) throws CloudProvisioningException {
try {
validationContext.validationOngoingEvent(ValidationMessageType.TOP_LEVEL_VALIDATION_MESSAGE,
getFormattedMessage("validating_compute_quotas"));
final ComputeLimits limits = this.computeApi.getLimits();
if (limits == null) {
throw new OpenstackException("Error requesting compute limits");
}
final int coreLimit = limits.getLimits().getMaxTotalCores();
final int ramLimit = limits.getLimits().getMaxTotalRAMSize();
final int instanceLimit = limits.getLimits().getMaxTotalInstances();
final List<NovaServer> servers = this.computeApi.getServers();
if (servers == null) {
throw new CloudProvisioningException("Failed requesting list of servers");
}
// if instanceLimit is 0 this is either a mock object or we failed to retrieve the limit -> warn and skip
if (instanceLimit == 0) {
logger.warning("Failed to retrieve instances quota, skipping instance quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateInstanceQuota(validationContext, instanceLimit, servers);
}
if (coreLimit == 0 && ramLimit == 0) {
logger.warning("Failed to retrieve vCPUs and RAM quotas, skipping vCPUs and RAM validations");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateCoreAndRamQuota(validationContext, managementComputeTemplate, coreLimit, ramLimit, servers);
}
// the validating_compute_quotas event completed successfully
validationContext.validationEventEnd(ValidationResultType.OK);
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.WARNING);
logger.log(Level.WARNING, "Cannot validate compute quotas on this provider.");
}
}
private void validateCoreAndRamQuota(final ValidationContext validationContext,
final ComputeTemplate managementComputeTemplate,
final int coreLimit, final int ramLimit, final List<NovaServer> servers)
throws CloudProvisioningException {
int existingVcpus = 0;
int existingRam = 0;
List<Flavor> flavors;
try {
flavors = computeApi.getFlavors();
} catch (final OpenstackException e) {
throw new CloudProvisioningException("Error requesting flavors.", e);
}
for (final NovaServer novaServer : servers) {
NovaServer serverDetails = null;
try {
serverDetails = this.computeApi.getServerDetails(novaServer.getId());
} catch (final OpenstackException e) {
throw new CloudProvisioningException("Error requesting server details", e);
}
if (serverDetails == null) {
throw new CloudProvisioningException("Error requesting server details for server with ID: "
+ novaServer.getId());
}
// Getting flavor details (vCPUs and RAM) for the current server and sum them
logger.finest("Getting flavor details for server instance with ID: " + novaServer.getId());
final Flavor flavor = getFlavorById(flavors, serverDetails.getFlavor().getId());
existingVcpus += flavor.getVcpus();
existingRam += flavor.getRam();
}
// get the management flavor and number of machines
final String managementHardwareId = managementComputeTemplate.getHardwareId().split("/")[1];
final Flavor managementFlavor = getFlavorById(flavors, managementHardwareId);
final int numOfManagementMachines = cloud.getProvider().getNumberOfManagementMachines();
// calculate the required vCPUs for the management machine(s), unless quota was not retrieved
if (coreLimit == 0) {
logger.warning("Failed to retrieve vCPUs quota, skipping vCPUs quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateCpusQuota(validationContext, coreLimit, existingVcpus, managementFlavor.getVcpus(),
numOfManagementMachines);
}
// calculate the required RAM for the management machine(s), unless quota was not retrieved
if (ramLimit == 0) {
logger.warning("Failed to retrieve RAM quota, skipping RAM quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateRamQuota(validationContext, ramLimit, existingRam, managementFlavor.getRam(),
numOfManagementMachines);
}
}
private void validateCpusQuota(final ValidationContext validationContext, final int coreLimit,
final int existingVcpus, final int managementFlavorVcpus, final int numOfManagementMachines)
throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_total_cores_quota"));
logger.finest("virtual-cores quota limit is: " + coreLimit);
final int requiredVcpus = managementFlavorVcpus * numOfManagementMachines;
logger.finest("used cores amount to: " + existingVcpus + ". Required: " + requiredVcpus);
// if vCPUs limit is unlimited (-1) - skip the calculation
if (coreLimit != UNLIMITED_RESOURCE_QUOTA) {
if (existingVcpus + requiredVcpus > coreLimit) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"virtual CPUs", coreLimit, existingVcpus, requiredVcpus));
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateRamQuota(final ValidationContext validationContext, final int ramLimit, int existingRam,
final int managementFlavorRAM, final int numOfManagementMachines) throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_total_ram_quota"));
logger.finest("RAM quota limit is: " + ramLimit);
final int requiredRam = managementFlavorRAM * numOfManagementMachines;
logger.finest("used RAM amounts to: " + existingRam + ". Required: " + requiredRam);
// if ram limit is unlimited (-1) - skip the calculation
if (ramLimit != UNLIMITED_RESOURCE_QUOTA) {
if (existingRam + requiredRam > ramLimit) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"RAM", ramLimit, existingRam, requiredRam));
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private Flavor getFlavorById(final List<Flavor> flavors, final String flavorId)
throws CloudProvisioningException {
for (final Flavor flavor : flavors) {
if (flavor.getId().equals(flavorId)) {
return flavor;
}
}
throw new CloudProvisioningException("Could not find flavor with ID: " + flavorId);
}
private void validateInstanceQuota(final ValidationContext validationContext,
final int instanceLimit, final List<NovaServer> servers) throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_total_instances_quota"));
logger.finest("server instance quota is: " + instanceLimit);
if (instanceLimit == UNLIMITED_RESOURCE_QUOTA) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
final int numOfManagementMachines = cloud.getProvider().getNumberOfManagementMachines();
final int numOfActiveServers = servers.size();
logger.finest("active server instance count is: " + servers.size()
+ ". Required: " + numOfManagementMachines);
if (numOfManagementMachines + numOfActiveServers > instanceLimit) {
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"server instances", instanceLimit, numOfActiveServers, numOfManagementMachines));
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
}
private void validateNetworkQuotas(final ValidationContext validationContext) throws CloudProvisioningException {
try {
validationContext.validationOngoingEvent(ValidationMessageType.TOP_LEVEL_VALIDATION_MESSAGE,
getFormattedMessage("validating_network_quotas"));
// get the tenant id (same value for compute and network)
final String tenantId = computeApi.getTenantId();
if (StringUtils.isBlank(tenantId)) {
validationContext.validationEventEnd(ValidationResultType.WARNING);
logger.warning("Failed to retrieve tenant id, skipping network quotas validation");
return;
}
// retrieve the quotas for this tenant
final Quota quotas = this.networkApi.getQuotasForTenant(tenantId);
if (quotas == null) {
throw new OpenstackException("Failed getting network quotas.");
}
// if instanceLimit is 0 this is either a mock object or we failed to retrieve the limit -> warn and skip
if (quotas.getSecurityGroup() == 0) {
logger.warning("Failed to retrieve security-group quota, skipping security-group quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateSecurityGroupsQuota(validationContext, quotas.getSecurityGroup(), tenantId);
}
if (quotas.getSecurityGroupRule() == 0) {
logger.warning("Failed to retrieve security-group rules quota, "
+ "skipping security-group rule quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateSecurityGroupRulesQuota(validationContext, quotas.getSecurityGroupRule(), tenantId);
}
if (quotas.getRouter() == 0) {
logger.warning("Failed to retrieve routers quota, skipping routers quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateRoutersQuota(validationContext, quotas.getRouter(), tenantId);
}
if (quotas.getNetwork() == 0) {
logger.warning("Failed to retrieve networks quota, skipping networks quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateNetworksQuota(validationContext, quotas.getNetwork(), tenantId);
}
if (quotas.getSubnet() == 0) {
logger.warning("Failed to retrieve subnets quota, skipping subnets quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateSubnetsQuota(validationContext, quotas.getSubnet(), tenantId);
}
if (quotas.getFloatingip() == 0) {
logger.warning("Failed to retrieve floating-ip quota, skipping floating-ip quota validation");
validationContext.validationEventEnd(ValidationResultType.WARNING);
} else {
validateFloatingIpsQuota(validationContext, quotas.getFloatingip());
}
validationContext.validationEventEnd(ValidationResultType.OK);
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.WARNING);
logger.info("Cannot validate network quotas on this provider.");
}
}
private void validateSecurityGroupsQuota(final ValidationContext validationContext, final int securityGroupsQuota,
final String tenantId) throws CloudProvisioningException {
int securityGroupsUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_security_groups_quota"));
logger.finest("security groups quota: " + securityGroupsQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == securityGroupsQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for security groups, validate it will not be exceeded
int plannedSecurityGroupsCount = DEFAULT_SECURITY_GROUPS_COUNT;
logger.finest("planned security groups count: " + plannedSecurityGroupsCount);
try {
final List<SecurityGroup> securityGroups = this.networkApi.getSecurityGroupsByTenantId(tenantId);
if (securityGroups != null) {
securityGroupsUsage = securityGroups.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error requesting security groups.", e);
}
logger.finest("security groups usage: " + securityGroupsUsage);
// compare what we need against the quota - usage
if (securityGroupsQuota - securityGroupsUsage >= plannedSecurityGroupsCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"Security-groups", securityGroupsQuota, securityGroupsUsage, plannedSecurityGroupsCount));
}
}
}
private void validateSecurityGroupRulesQuota(final ValidationContext validationContext, final int rulesQuota,
final String tenantId) throws CloudProvisioningException {
int rulesUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_security_group_rules_quota"));
logger.finest("security-group rules quota: " + rulesQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == rulesQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for security groups, validate it will not be exceeded
int plannedSecurityRulesCount = DEFAULT_SECURITY_GROUP_RULES;
logger.finest("planned security-groups rule count: " + plannedSecurityRulesCount);
try {
final List<SecurityGroupRule> securityRules = networkApi.getSecurityGroupRulesByTenantId(tenantId);
if (securityRules != null) {
rulesUsage = securityRules.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error requesting security groups rules", e);
}
logger.finest("security-group rules usage: " + rulesUsage);
// compare what we need against the quota - usage
if (rulesQuota - rulesUsage >= plannedSecurityRulesCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"Security-group rules", rulesQuota, rulesUsage, plannedSecurityRulesCount));
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateRoutersQuota(final ValidationContext validationContext, final int routersQuota,
final String tenantId) throws CloudProvisioningException {
int routersUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_routers"));
logger.finest("routers quota: " + routersQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == routersQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for routers, validate it if Cloudify is configured to create a router
if (!networkHelper.isCreateExternalRouter()) {
// the cloud driver is using an existing router
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// the cloud driver will attempt to create 1 external router
int plannedRoutersCount = DEFAULT_ROUTERS_COUNT;
logger.finest("planned routers count: " + plannedRoutersCount);
try {
List<Router> routers = networkApi.getRoutersByTenantId(tenantId);
if (routers != null) {
routersUsage = routers.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error getting routers usage from openstack", e);
}
logger.finest("routers usage: " + routersUsage);
// compare what we need against the quota - usage
if (routersQuota - routersUsage >= plannedRoutersCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"routers", routersQuota, routersUsage, plannedRoutersCount));
}
}
}
}
private void validateNetworksQuota(final ValidationContext validationContext, final int networksQuota,
final String tenantId) throws CloudProvisioningException {
int networksUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_networks"));
logger.finest("networks quota: " + networksQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == networksQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for networks, validate it if Cloudify is configured to create a network
if (!networkHelper.isCreateExternalNetwork()) {
// the cloud driver is using an existing network
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// the cloud driver will attempt to create 1 external network
int plannedNetworksCount = DEFAULT_MGMT_NETWORKS_COUNT;
logger.finest("planned management networks count: " + plannedNetworksCount);
try {
List<Network> networks = networkApi.getNetworksByTenantId(tenantId);
if (networks != null) {
networksUsage = networks.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error getting networks usage from openstack", e);
}
logger.finest("networks usage: " + networksUsage);
// compare what we need against the quota - usage
if (networksQuota - networksUsage >= plannedNetworksCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"networks", networksQuota, networksUsage, plannedNetworksCount));
}
}
}
}
private void validateSubnetsQuota(final ValidationContext validationContext, final int subnetsQuota,
final String tenantId) throws CloudProvisioningException {
int subnetsUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_subnets"));
logger.finest("subnets quota: " + subnetsQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == subnetsQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for subnets, validate it will not be exceeded
// TODO handle multiple subnets
int plannedSubnetsCount = this.cloud.getProvider().getNumberOfManagementMachines();
logger.finest("planned management subnets count: " + plannedSubnetsCount);
try {
List<Subnet> subnets = networkApi.getSubnetsByTenantId(tenantId);
if (subnets != null) {
subnetsUsage = subnets.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error getting subnets usage from openstack", e);
}
logger.finest("subnets usage: " + subnetsUsage);
// compare what we need against the quota - usage
if (subnetsQuota - subnetsUsage >= plannedSubnetsCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"subnets", subnetsQuota, subnetsUsage, plannedSubnetsCount));
}
}
}
private void validateFloatingIpsQuota(final ValidationContext validationContext, final int floatingIpsQuota)
throws CloudProvisioningException {
int floatingIpsUsage = 0;
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_floating_ips"));
logger.finest("floating IPs quota: " + floatingIpsQuota);
// if there is no quota (quota = -1) - no need to perform any comparison
if (UNLIMITED_RESOURCE_QUOTA == floatingIpsQuota) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
// quota defined for floating IPs, validate it if floating IPs association is set
if (!this.networkHelper.associateFloatingIp()) {
// the cloud driver is not associating floating IPs
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
int plannedFloatingIpsCount = this.cloud.getProvider().getNumberOfManagementMachines();
logger.finest("planned management floating IPs count: " + plannedFloatingIpsCount);
try {
List<FloatingIp> floatingIps = networkApi.getFloatingIps();
if (floatingIps != null) {
floatingIpsUsage = floatingIps.size();
}
} catch (OpenstackException e) {
throw new CloudProvisioningException("Error getting floating IPs usage from openstack", e);
}
logger.finest("floating IPs usage: " + floatingIpsUsage);
// compare what we need against the quota - usage
if (floatingIpsQuota - floatingIpsUsage >= plannedFloatingIpsCount) {
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(getFormattedMessage("resource_validation_failure",
"floating IPs", floatingIpsQuota, floatingIpsUsage, plannedFloatingIpsCount));
}
}
}
}
private void validateCredentials(final ValidationContext validationContext)
throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating credentials");
// test request information from openstack
try {
this.computeApi.getServers();
} catch (OpenstackServerException e) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
if (e.getStatusCode() == HTTP_AUTHENTIFICATION_ERROR) {
throw new CloudProvisioningException(
"Authentification operation failed. Please check credentials informations.");
}
throw new CloudProvisioningException("Could not request Openstack", e);
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException("Could not request Openstack", e);
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateTemplateNetworks(final ValidationContext validationContext, final CloudNetwork cloudNetwork,
final Collection<ComputeTemplate> templates)
throws CloudProvisioningException {
// boolean networkInCloud = false;
Map<String, NetworkConfiguration> templateNetworkConfigurations = cloudNetwork.getTemplates();
if (templateNetworkConfigurations != null && !templateNetworkConfigurations.isEmpty()) {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating templates network configuration");
for (Entry<String, NetworkConfiguration> networkConfigurationEntry : templateNetworkConfigurations
.entrySet()) {
if (!networkHelper.isValidNetworkName(networkConfigurationEntry.getValue())) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(String.format(
"The name of template network configuration is missing. "
+ "Please check template network in '%s'",
networkConfigurationEntry.getKey()));
}
List<org.cloudifysource.domain.cloud.network.Subnet> templateNetworkSubnets =
networkConfigurationEntry.getValue().getSubnets();
if (templateNetworkSubnets == null || templateNetworkSubnets.isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
String.format(
"Subnets list is empty. At least one subnet is required. "
+ "Please check template network configuration in '%s'.",
networkConfigurationEntry.getValue().getName()));
}
for (org.cloudifysource.domain.cloud.network.Subnet mSub : templateNetworkSubnets) {
if (!networkHelper.isValidSubnetName(mSub)) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
String.format(
"The name of the subnet is missing."
+ " Please check subnet name in template network "
+ "configuration '%s' ",
networkConfigurationEntry.getValue().getName()));
}
if (mSub.getRange() == null || StringUtils.trim(mSub.getRange()).isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
String.format(
"The range is missing in subnet '%s'. "
+ "Please check subnet range in template network "
+ "configuration '%s' ",
mSub.getName(), networkConfigurationEntry.getKey()));
}
}
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateManagementNetwork(final ValidationContext validationContext,
final ComputeTemplate managementComputeTemplate, final CloudNetwork cloudNetwork)
throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating management network configuration");
boolean isNetworkExistsForManager = false;
if (cloudNetwork != null) {
final ManagementNetwork managementNetwork = cloudNetwork.getManagement();
if (managementNetwork != null) {
final NetworkConfiguration managementNetworkConfiguration = managementNetwork.getNetworkConfiguration();
// network available for management
if (managementNetworkConfiguration != null
&& (managementNetworkConfiguration.getName() != null
|| (managementNetworkConfiguration.getSubnets() != null
&& !managementNetworkConfiguration.getSubnets().isEmpty())
|| !managementNetworkConfiguration.getCustom().isEmpty())) {
isNetworkExistsForManager = true;
final String mngNetName = managementNetworkConfiguration.getName();
if (mngNetName == null
|| StringUtils.isEmpty(mngNetName.trim())) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
"The name of Management network is missing. "
+ "Please check management network configuration in CloudNetwork block.");
}
// validating subnets for management network
List<org.cloudifysource.domain.cloud.network.Subnet> managementNetwokSubnets =
managementNetworkConfiguration.getSubnets();
if (managementNetwokSubnets != null) {
// no subnets defined in management network
if (managementNetwokSubnets.isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
String.format(
"Subnets list is empty. At least one subnet is required."
+ " Please check management network configuration in '%s'.",
mngNetName));
} else {
// subnets are defined
for (org.cloudifysource.domain.cloud.network.Subnet mSub : managementNetwokSubnets) {
if (!networkHelper.isValidSubnetName(mSub)) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(String.format("The name of subnet is missing."
+ " Please check subnet in management network "
+ "configuration '%s'.", managementNetwork.getNetworkConfiguration()
.getName()));
}
if (!networkHelper.isValidSubnetRange(mSub)) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
String.format(
"The range is missing in subnet '%s'. "
+ "Please check subnet range in management network "
+ "configuration.",
mSub.getName()));
}
}
}
}
}
}
}
if (!isNetworkExistsForManager) {
if (managementComputeTemplate != null) {
final ComputeTemplateNetwork computeNetwork = managementComputeTemplate.getComputeNetwork();
List<String> computeNetworks = computeNetwork.getNetworks();
if (computeNetworks != null && !computeNetworks.isEmpty()) {
isNetworkExistsForManager = true;
}
}
if (!isNetworkExistsForManager) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
"A network must be provided to the management machines "
+ "(use either cloudNetwork templates or computeNetwork configuration).");
}
}
// management network/ subnets are OK
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateOpenstackEndpoint(final ValidationContext validationContext,
final ComputeTemplate managementComputeTemplate) throws CloudProvisioningException {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating openstack endpoint property");
if (managementComputeTemplate.getOverrides() != null
&& !managementComputeTemplate.getOverrides().isEmpty()) {
String openstackProperty =
(String) managementComputeTemplate.getOverrides().get(OPENSTACK_ENDPOINT);
if (openstackProperty == null || openstackProperty.trim().isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException((String.format(
"The openstack endpoint '%s' is missing. "
+ "Please check overrides block in management template '%s'. ",
OPENSTACK_ENDPOINT, cloud.getConfiguration().getManagementMachineTemplate())));
}
validationContext.validationEventEnd(ValidationResultType.OK);
} else {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(String.format(
"The openstack endpoint option '%s' is missing. "
+ "Please check overrides block in management template ",
OPENSTACK_ENDPOINT));
}
}
private void validateComputeTemplates(final ValidationContext validationContext,
final String groovyFile,
final String propertiesFile,
final Map<String, ComputeTemplate> templates)
throws CloudProvisioningException {
ManagementNetwork managementNetwork = cloud.getCloudNetwork().getManagement();
boolean isManagementNetwork = true;
if (managementNetwork.getNetworkConfiguration() != null
&& managementNetwork.getNetworkConfiguration().getName() == null
&& managementNetwork.getNetworkConfiguration().getSubnets() != null
&& managementNetwork.getNetworkConfiguration().getSubnets().isEmpty()) {
isManagementNetwork = false;
}
List<String> missingNetworks = new ArrayList<String>();
for (Entry<String, ComputeTemplate> entry : templates.entrySet()) {
final ComputeTemplate computeTemplate = entry.getValue();
String templateName = entry.getKey();
validationContext.validationEvent(ValidationMessageType.GROUP_VALIDATION_MESSAGE,
getFormattedMessage("validating_template", templateName));
final String imageLocation = computeTemplate.getImageId();
if (!imageLocation.contains("/")) {
throw new CloudProvisioningException("'imageId' should be formatted as region/imageId."
+ " Verify the cloud template : " + templateName);
}
final String hardwareLocation = computeTemplate.getHardwareId();
if (!hardwareLocation.contains("/")) {
throw new CloudProvisioningException("'hardwareId' should be formatted as region/flavorId."
+ " Verify the cloud template : " + templateName);
}
this.validateImageHardwareLocation(validationContext, groovyFile, propertiesFile, computeTemplate);
// validating static securityGroupNames
this.validateStaticSecgroups(validationContext, groovyFile, propertiesFile, computeTemplate);
// validating static network
this.validateStaticNetworks(validationContext, groovyFile, propertiesFile, computeTemplate);
if (!isManagementNetwork && computeTemplate.getComputeNetwork() != null) {
final List<String> networks = computeTemplate.getComputeNetwork().getNetworks();
if (networks == null || networks.isEmpty()) {
missingNetworks.add(templateName);
}
}
}
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating cloud compute network configuration");
if (!missingNetworks.isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
if (missingNetworks.size() == 1) {
throw new CloudProvisioningException(
"Since management network is missing, a network must be provided for the template: "
+ missingNetworks.get(0));
} else {
throw new CloudProvisioningException(
"Since management network is missing, a network must be provided for all templates: "
+ missingNetworks);
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private void validateStaticNetworks(final ValidationContext validationContext, final String groovyFile,
final String propertiesFile, final ComputeTemplate computeTemplate) throws CloudProvisioningException {
final List<String> networks = computeTemplate.getComputeNetwork().getNetworks();
if (networks != null && !networks.isEmpty()) {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
"Validating network(s): " + networks.toString());
try {
final Set<String> missingList = new HashSet<String>();
final List<Network> existingList = networkApi.getNetworks();
for (final String networkName : networks) {
boolean found = false;
if (existingList != null) {
for (final Network network : existingList) {
if (networkName.equals(network.getName())) {
found = true;
break;
}
}
}
if (!found || existingList == null || existingList.isEmpty()) {
missingList.add(networkName);
}
}
if (!missingList.isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
if (missingList.size() == 1) {
throw new CloudProvisioningException(String.format(
"Network \"%s\" does not exist. Please create it or rename in %s or in %s",
missingList.iterator().next(), groovyFile, propertiesFile));
} else if (missingList.size() > 1) {
throw new CloudProvisioningException(String.format(
"Networks %s do not exist. Please create them or rename in %s or in %s",
Arrays.toString(missingList.toArray()), groovyFile, propertiesFile));
}
}
} catch (final OpenstackException ex) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException("Error requesting networks.", ex);
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
}
private void validateStaticSecgroups(final ValidationContext validationContext, final String groovyFile,
final String propertiesFile, final ComputeTemplate computeTemplate) throws CloudProvisioningException {
final Map<String, Object> computeOptions = computeTemplate.getOptions();
if (computeOptions != null) {
Object securityGroups = computeOptions.get("securityGroupNames");
if (securityGroups == null) {
securityGroups = computeOptions.get("securityGroups");
}
if (securityGroups != null) {
if (securityGroups instanceof String[] && ((String[]) securityGroups).length > 0) {
final String[] scgArray = (String[]) securityGroups;
if (scgArray.length == 1) {
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_security_group", scgArray[0]));
} else {
validationContext.validationOngoingEvent(
ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_security_groups",
org.cloudifysource.esc.util.StringUtils.arrayToString(scgArray, ", ")));
}
try {
final Set<String> missingList = new HashSet<String>();
final List<SecurityGroup> existingList = networkApi.getSecurityGroups();
for (int i = 0; i < scgArray.length; i++) {
boolean found = false;
if (existingList != null) {
for (final SecurityGroup existing : existingList) {
if (scgArray[i].equals(existing.getName())) {
found = true;
break;
}
}
}
if (!found || existingList == null || existingList.isEmpty()) {
missingList.add(scgArray[i]);
}
}
if (!missingList.isEmpty()) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
if (missingList.size() == 1) {
throw new CloudProvisioningException(getFormattedMessage(
"error_security_group_validation",
missingList.iterator().next(), groovyFile, propertiesFile));
} else if (missingList.size() > 1) {
throw new CloudProvisioningException(getFormattedMessage(
"error_security_groups_validation",
Arrays.toString(missingList.toArray()), groovyFile, propertiesFile));
}
}
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException("Error requesting security groups.", e);
}
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
}
}
private void validateImageHardwareLocation(final ValidationContext validationContext, final String groovyFile,
final String propertiesFile, final ComputeTemplate computeTemplate) throws CloudProvisioningException {
final String imageId = computeTemplate.getImageId().split("/")[1];
final String hardwareId = computeTemplate.getHardwareId().split("/")[1];
final String locationId = computeTemplate.getImageId().split("/")[0];
validationContext.validationOngoingEvent(ValidationMessageType.ENTRY_VALIDATION_MESSAGE,
getFormattedMessage("validating_image_hardware_location_combination",
imageId == null ? "" : imageId, hardwareId == null ? "" : hardwareId,
locationId == null ? "" : locationId));
// validating imageIds
try {
if (imageId != null) {
try {
computeApi.getImage(imageId);
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
final String availableResources = this.formatResourceList(computeApi.getImages());
throw new CloudProvisioningException(
getFormattedMessage("error_image_id_validation",
imageId == null ? "" : imageId, availableResources));
}
}
// validating hardwareId
if (hardwareId != null) {
try {
computeApi.getFlavor(hardwareId);
} catch (final OpenstackException e) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
final String availableResources = this.formatResourceList(computeApi.getFlavors());
throw new CloudProvisioningException(
getFormattedMessage("error_hardware_id_validation",
hardwareId == null ? "" : hardwareId, availableResources));
}
}
} catch (final OpenstackException ex) {
validationContext.validationEventEnd(ValidationResultType.ERROR);
throw new CloudProvisioningException(
getFormattedMessage("error_image_hardware_location_combination_validation",
imageId == null ? "" : imageId,
hardwareId == null ? "" : hardwareId, locationId == null ? "" : locationId,
groovyFile, propertiesFile), ex);
}
validationContext.validationEventEnd(ValidationResultType.OK);
}
private String formatResourceList(final List<?> resources) {
final StringBuilder sb = new StringBuilder();
for (final Object resource : resources) {
sb.append(System.getProperty("line.separator"));
sb.append(resource);
}
return sb.toString();
}
}