/******************************************************************************* * Copyright (c) 2011 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.rest.util; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.internal.CloudifyConstants.DeploymentState; import org.cloudifysource.dsl.internal.CloudifyConstants.USMState; import org.cloudifysource.dsl.rest.response.ApplicationDescription; import org.cloudifysource.dsl.rest.response.InstanceDescription; import org.cloudifysource.dsl.rest.response.ServiceDescription; import org.cloudifysource.dsl.utils.ServiceUtils; import org.cloudifysource.dsl.utils.ServiceUtils.FullServiceName; import org.cloudifysource.rest.exceptions.ResourceNotFoundException; import org.openspaces.admin.Admin; import org.openspaces.admin.AdminException; import org.openspaces.admin.application.Application; import org.openspaces.admin.application.Applications; import org.openspaces.admin.internal.pu.DefaultProcessingUnit; import org.openspaces.admin.pu.DeploymentStatus; import org.openspaces.admin.pu.ProcessingUnit; import org.openspaces.admin.pu.ProcessingUnitInstance; import org.openspaces.admin.pu.ProcessingUnitInstanceStatistics; import org.openspaces.admin.pu.ProcessingUnitType; import org.openspaces.admin.pu.ProcessingUnits; import org.openspaces.admin.zone.Zone; import org.openspaces.admin.zone.Zones; import org.openspaces.core.properties.BeanLevelProperties; import org.openspaces.pu.service.ServiceMonitors; /** * This factory class is responsible for manufacturing an application description POJO. The application description will * consist of all the application's services and their status. The application status is made out of an intersection * between all of it's service's status. A service status is determined by the status of all it's service instances. * * @author adaml * */ public class ApplicationDescriptionFactory { private final Admin admin; private static final Logger logger = Logger .getLogger(ApplicationDescriptionFactory.class.getName()); private static final String NOT_AVAILABLE_STATE = "NA"; public ApplicationDescriptionFactory(final Admin admin) { this.admin = admin; } /** * returns a list of application description POJOs. * * @return a list of the application descriptions. */ public List<ApplicationDescription> getApplicationDescriptions() { final Applications applications = admin.getApplications(); List<ApplicationDescription> applicationDescriptions = new ArrayList<ApplicationDescription>(); for (Application application : applications) { if (!application.getName().equalsIgnoreCase(CloudifyConstants.MANAGEMENT_APPLICATION_NAME)) { applicationDescriptions.add(getApplicationDescription(application)); } } return applicationDescriptions; } /** * returns an application description POJO. * * @param applicationName * The application name. * @return the application description. * @throws ResourceNotFoundException * Thrown if a matching application was not found */ public ApplicationDescription getApplicationDescription(final String applicationName) throws ResourceNotFoundException { final Application application = admin.getApplications().getApplication(applicationName); if (application == null) { throw new ResourceNotFoundException(applicationName); } return getApplicationDescription(application); } /** * returns an application description POJO. * * @param application * the application name. * @return the application description. */ public ApplicationDescription getApplicationDescription(final Application application) { String applicationName = application.getName(); final ApplicationDescription applicationDescription = new ApplicationDescription(); List<ServiceDescription> serviceDescriptionList = getServicesDescription(application); logger.log(Level.FINE, "Creating application description for application " + applicationName); final DeploymentState applicationState = getApplicationState(serviceDescriptionList); applicationDescription.setApplicationName(applicationName); applicationDescription.setAuthGroups(getApplicationAuthorizationGroups(application)); applicationDescription.setServicesDescription(serviceDescriptionList); applicationDescription.setApplicationState(applicationState); return applicationDescription; } /** * Gets a list of {@link ServiceDescription} objects, representing the application's services. * * @param app * The {@link Application} object of the application containing the services * @return a list of {@link ServiceDescription} objects, representing the application's services. */ private List<ServiceDescription> getServicesDescription(final Application app) { List<ServiceDescription> serviceDescriptionList = new ArrayList<ServiceDescription>(); final ProcessingUnits pus = app.getProcessingUnits(); for (final ProcessingUnit pu : pus) { final ServiceDescription serviceDescription = getServiceDescription(pu); serviceDescriptionList.add(serviceDescription); } return serviceDescriptionList; } /** * Gets the {@link ServiceDescription} object of the given processingUnit. * @param processingUnit the processingUnit * @return {@link ServiceDescription} object. */ public ServiceDescription getServiceDescription(final ProcessingUnit processingUnit) { int plannedNumberOfInstances, numberOfServiceInstances; DeploymentState serviceState; ServiceDescription serviceDescription = new ServiceDescription(); // TODO noak is it ok to exclude the management PUs here? the code didn't support it to begin with. plannedNumberOfInstances = getPlannedNumberOfInstances(processingUnit); numberOfServiceInstances = getNumberOfServiceInstances(processingUnit); List<InstanceDescription> serviceInstancesDescription = getServiceInstacesDescription(processingUnit); serviceState = getServiceState(processingUnit, serviceInstancesDescription, numberOfServiceInstances, plannedNumberOfInstances); String absolutePuName = processingUnit.getName(); logger.log(Level.FINE, "Service \"" + absolutePuName + "\" is in state: " + serviceState); serviceDescription.setPlannedInstances(plannedNumberOfInstances); serviceDescription.setInstanceCount(numberOfServiceInstances); FullServiceName fullServiceName = ServiceUtils.getFullServiceName(processingUnit.getName()); final String applicationName = fullServiceName.getApplicationName(); final String serviceName = fullServiceName.getServiceName(); serviceDescription.setApplicationName(applicationName); serviceDescription.setServiceName(serviceName); serviceDescription.setInstancesDescription(serviceInstancesDescription); serviceDescription.setServiceState(serviceState); final String deploymentId = processingUnit.getBeanLevelProperties() .getContextProperties().getProperty(CloudifyConstants.CONTEXT_PROPERTY_DEPLOYMENT_ID); serviceDescription.setDeploymentId(deploymentId); return serviceDescription; } /** * Gets the {@link ServiceDescription} object of the given zone. * This method is typically called when the ProcessingUnit object is not available, * i.e. during service undeploy, after the pu was already uninstalled. * @param zone the zone * @return {@link ServiceDescription} object. */ public ServiceDescription getServiceDescription(final Zone zone) { int plannedNumberOfInstances, numberOfServiceInstances; DeploymentState serviceState; ServiceDescription serviceDescription = new ServiceDescription(); plannedNumberOfInstances = 0; numberOfServiceInstances = zone.getProcessingUnitInstances().length; List<InstanceDescription> serviceInstancesDescription = getServiceInstacesDescription(zone); serviceState = DeploymentState.IN_PROGRESS; //since this method is called during uninstall logger.log(Level.FINE, "Service \"" + zone.getName() + "\" is in state: " + serviceState); serviceDescription.setPlannedInstances(plannedNumberOfInstances); serviceDescription.setInstanceCount(numberOfServiceInstances); FullServiceName fullServiceName = ServiceUtils.getFullServiceName(zone.getName()); final String applicationName = fullServiceName.getApplicationName(); final String serviceName = fullServiceName.getServiceName(); serviceDescription.setApplicationName(applicationName); serviceDescription.setServiceName(serviceName); serviceDescription.setInstancesDescription(serviceInstancesDescription); serviceDescription.setServiceState(serviceState); String deploymentId = getDeploymentIdFromServiceInstaces(zone); serviceDescription.setDeploymentId(deploymentId); return serviceDescription; } /** * Gets a populated service description object for the specified service. * * @param absolutePuName * The full service name (<application name>.<service name>) * @return A populated service description object * @throws ResourceNotFoundException * Thrown if a matching service was not found */ public ServiceDescription getServiceDescription(final String absolutePuName) throws ResourceNotFoundException { Zone zone; ProcessingUnit processingUnit = null; zone = getZone(absolutePuName); if (zone != null) { // for undeploy - zone exists, PU does not. ProcessingUnitInstance[] processingUnitInstances = zone.getProcessingUnitInstances(); if (processingUnitInstances.length > 0) { processingUnit = processingUnitInstances[0].getProcessingUnit(); } } // if PU not found in zone, perhaps GSCs are not started yet, so look for PU in Admin. if (processingUnit == null) { // for deploy - zone does not exist, PU does. processingUnit = admin.getProcessingUnits().getProcessingUnit(absolutePuName); } if (processingUnit != null) { return getServiceDescription(processingUnit); } else if (processingUnit == null && zone != null) { // this could happen on uninstall, if the pu is down already but the zone is not return getServiceDescription(zone); } else { // both pu and zone are null throw new ResourceNotFoundException(absolutePuName); } } /** * Gets a populated {@link InstanceDescription} object describing the instance (name, id, state, etc.). * * @param processingUnitInstance * The processing unit instance to describe * @return a populated {@link InstanceDescription} object describing the instance (name, id, state, etc.) */ private InstanceDescription getInstanceDescription(final ProcessingUnitInstance processingUnitInstance) { String instanceState = getInstanceState(processingUnitInstance); final int instanceId = processingUnitInstance.getInstanceId(); final String instanceHostName = processingUnitInstance.getVirtualMachine().getMachine().getHostName(); final String instanceHostAddress = processingUnitInstance.getVirtualMachine().getMachine().getHostAddress(); final String instanceName = processingUnitInstance.getName(); final InstanceDescription instanceDescription = new InstanceDescription(); instanceDescription.setInstanceStatus(instanceState != null ? instanceState : NOT_AVAILABLE_STATE); instanceDescription.setInstanceName(instanceName); instanceDescription.setInstanceId(instanceId); instanceDescription.setHostName(instanceHostName); instanceDescription.setHostAddress(instanceHostAddress); return instanceDescription; } /** * Get the state of a processing unit instance. * * @param processingUnitInstance * The processing unit instance to examine * @return the state of a processing unit instance. */ private String getInstanceState( final ProcessingUnitInstance processingUnitInstance) { String instanceState; final ProcessingUnit processingUnit = processingUnitInstance.getProcessingUnit(); if (processingUnit.getType() == ProcessingUnitType.UNIVERSAL) { USMState instanceUsmState = getInstanceUsmState(processingUnitInstance); if (instanceUsmState == null) { return null; } instanceState = instanceUsmState.toString(); } else { instanceState = processingUnit.getStatus().toString(); } return instanceState; } /** * Gets a list of {@link InstanceDescription} objects, describing the service instances. * * @param processingUnit * The service's processing unit of which instances are described * @return a list of {@link InstanceDescription} objects, describing the service instances. */ private List<InstanceDescription> getServiceInstacesDescription( final ProcessingUnit processingUnit) { List<InstanceDescription> instancesDescriptionList = new ArrayList<InstanceDescription>(); if (processingUnit != null) { for (ProcessingUnitInstance processingUnitInstance : processingUnit.getInstances()) { InstanceDescription instanceDescription = getInstanceDescription(processingUnitInstance); instancesDescriptionList.add(instanceDescription); } } return instancesDescriptionList; } /** * Gets a list of {@link InstanceDescription} objects, describing the service instances. * * @param zone * The service's zone of which instances are described * @return a list of {@link InstanceDescription} objects, describing the service instances. */ private List<InstanceDescription> getServiceInstacesDescription( final Zone zone) { List<InstanceDescription> instancesDescriptionList = new ArrayList<InstanceDescription>(); if (zone != null) { for (ProcessingUnitInstance processingUnitInstance : zone.getProcessingUnitInstances()) { InstanceDescription instanceDescription = getInstanceDescription(processingUnitInstance); instancesDescriptionList.add(instanceDescription); } } return instancesDescriptionList; } /** * Gets a list of {@link InstanceDescription} objects, describing the service instances. * * @param zone * The service's zone of which instances are described * @return a list of {@link InstanceDescription} objects, describing the service instances. */ private String getDeploymentIdFromServiceInstaces( final Zone zone) { String deploymentId = null; if (zone != null) { for (ProcessingUnitInstance processingUnitInstance : zone.getProcessingUnitInstances()) { BeanLevelProperties beanLevelProps = processingUnitInstance.getProperties(); if (beanLevelProps != null) { Properties ctxProps = beanLevelProps.getContextProperties(); if (ctxProps != null) { deploymentId = ctxProps.getProperty(CloudifyConstants.CONTEXT_PROPERTY_DEPLOYMENT_ID); } } if (deploymentId != null) { break; } } } return deploymentId; } /** * Gets the application state - STARTED, INSTALLING or FAILED. The method returns FAILED status only if all of the * services reached a final state. * * @param serviceDescriptionList * a list of {@link ServiceDescription} objects, representing the application's services' state. * @return The applications' state */ private DeploymentState getApplicationState( final List<ServiceDescription> serviceDescriptionList) { logger.log(Level.FINE, "Determining services deployment state"); boolean servicesStillInstalling = false; boolean atLeastOneServiceFailed = false; for (final ServiceDescription serviceDescription : serviceDescriptionList) { logger.log(Level.FINE, "checking status for service " + serviceDescription.getServiceName()); if (serviceDescription.getServiceState() == DeploymentState.IN_PROGRESS || serviceDescriptionList.isEmpty()) { servicesStillInstalling = true; } if (serviceDescription.getServiceState() == DeploymentState.FAILED) { atLeastOneServiceFailed = true; } } if (servicesStillInstalling) { return DeploymentState.IN_PROGRESS; } if (atLeastOneServiceFailed) { return DeploymentState.FAILED; } return DeploymentState.STARTED; } /** * Gets a zone by its name. * * @param zoneName * The name of the requested zone * @return The zone matching the specified name, if found. Null otherwise. */ private Zone getZone(final String zoneName) { Zone zone = null; Zones zones = admin.getZones(); if (zones != null) { zone = zones.getByName(zoneName); } return zone; } /** * Gets the state of the specified service - STARTED, INSTALLING. If the zone is null - the service state is * uninstalled If the zone is not null but the PU is null- the service is currently being uninstalled. If PU * instances are found - the service is either running, installing, or in error, depending on the instances' states. * * @param processingUnit * the service's processing unit (optionally null) * @param serviceInstancesStatus * a list of {@link InstanceDescription} objects representing the service's instances' state * @return DeploymentState populated with service state details */ private DeploymentState getServiceState( final ProcessingUnit processingUnit, final List<InstanceDescription> serviceInstancesStatus, final int numberOfServiceInstances, final int plannedNumberOfInstances) { if (numberOfServiceInstances > plannedNumberOfInstances) { return DeploymentState.IN_PROGRESS; } // PU instances found - the service is either running, installing, or in error if (processingUnit.getType() == ProcessingUnitType.UNIVERSAL) { for (InstanceDescription instanceDescription : serviceInstancesStatus) { String instanceState = instanceDescription.getInstanceStatus(); if (instanceState.equals(USMState.ERROR.toString())) { return DeploymentState.FAILED; } } if (numberOfServiceInstances < plannedNumberOfInstances) { return DeploymentState.IN_PROGRESS; } return DeploymentState.STARTED; } else { // The service is not a USM service. if (processingUnit.getStatus() != DeploymentStatus.INTACT) { return DeploymentState.IN_PROGRESS; } else { return DeploymentState.STARTED; } } } /** * Gets a service's number of instances. * * @param processingUnit * The processing unit implementing this service * @return the planned number of instances for the specified service (PU) */ private int getNumberOfServiceInstances(final ProcessingUnit processingUnit) { if (processingUnit != null) { if (processingUnit.getType() == ProcessingUnitType.UNIVERSAL) { return getNumberOfUSMServicesWithRunningState(processingUnit); } return processingUnit.getInstances().length; } return 0; } /** * Gets a service's planned number of instances. * * @param processingUnit * The processing unit implementing this service * @return the planned number of instances for the specified service (PU) */ private int getPlannedNumberOfInstances(final ProcessingUnit processingUnit) { if (processingUnit == null) { return 0; } Map<String, String> elasticProperties = ((DefaultProcessingUnit) processingUnit).getElasticProperties(); int plannedNumberOfInstances; if (elasticProperties.containsKey("schema")) { String clusterSchemaValue = elasticProperties.get("schema"); if ("partitioned-sync2backup".equals(clusterSchemaValue)) { plannedNumberOfInstances = processingUnit.getTotalNumberOfInstances(); } else { plannedNumberOfInstances = processingUnit.getNumberOfInstances(); } } else { plannedNumberOfInstances = processingUnit.getNumberOfInstances(); } return plannedNumberOfInstances; } /** * Gets the number of RUNNING processing unit instances. * * @param processingUnit * the PU to examine * @return the number of RUNNING processing unit instances. */ private int getNumberOfUSMServicesWithRunningState( final ProcessingUnit processingUnit) { int puInstanceCounter = 0; if (processingUnit != null) { for (ProcessingUnitInstance pui : processingUnit.getInstances()) { if (isUsmStateOfPuiRunning(pui)) { puInstanceCounter++; } } } return puInstanceCounter; } private boolean isUsmStateOfPuiRunning(final ProcessingUnitInstance pui) { boolean isInstanceRunning = false; try { USMState instanceState = getInstanceUsmState(pui); if (instanceState != null) { isInstanceRunning = (instanceState == CloudifyConstants.USMState.RUNNING); } } catch (AdminException e) { // instance is not available for monitoring, so it's not considered running logger.finest("instance is not available for monitoring, so it's not considered running. " + "reported error: " + e.getMessage()); } return isInstanceRunning; } /** * Gets a PU instance's USM state. * * @param pui * the PU instance to examine * @return the USM state of the specified PU instance */ private USMState getInstanceUsmState(final ProcessingUnitInstance pui) { final ProcessingUnitInstanceStatistics statistics = pui.getStatistics(); if (statistics == null) { return null; } final Map<String, ServiceMonitors> puMonitors = statistics.getMonitors(); if (puMonitors == null) { return null; } final ServiceMonitors serviceMonitors = puMonitors.get("USM"); if (serviceMonitors == null) { return null; } final Map<String, Object> monitors = serviceMonitors.getMonitors(); if (monitors == null) { return null; } return USMState.values()[(Integer) monitors.get(CloudifyConstants.USM_MONITORS_STATE_ID)]; } private String getApplicationAuthorizationGroups(final Application application) { String appAuthGroups = ""; // getting the application's authGroups from its first service, // assuming they all have the same authorization groups. final ProcessingUnit pu = application.getProcessingUnits().iterator().next(); if (pu != null) { appAuthGroups = pu.getBeanLevelProperties().getContextProperties(). getProperty(CloudifyConstants.CONTEXT_PROPERTY_AUTH_GROUPS); } return appAuthGroups; } }