/* * Copyright 2015 the original author or authors. * * 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.springframework.xd.dirt.server.admin.deployment.zk; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.Assert; import org.springframework.xd.dirt.core.DeploymentUnitStatus; import org.springframework.xd.dirt.core.Job; import org.springframework.xd.dirt.core.JobDeploymentsPath; import org.springframework.xd.dirt.core.Stream; import org.springframework.xd.dirt.core.StreamDeploymentsPath; import org.springframework.xd.dirt.job.JobFactory; import org.springframework.xd.dirt.server.admin.deployment.DeploymentUnitStateCalculator; import org.springframework.xd.dirt.server.admin.deployment.DeploymentUnitType; import org.springframework.xd.dirt.server.admin.deployment.ModuleDeploymentStatus; import org.springframework.xd.dirt.stream.StreamFactory; import org.springframework.xd.dirt.zookeeper.ChildPathIterator; import org.springframework.xd.dirt.zookeeper.Paths; import org.springframework.xd.dirt.zookeeper.ZooKeeperConnection; import org.springframework.xd.dirt.zookeeper.ZooKeeperUtils; import org.springframework.xd.module.ModuleDescriptor; import org.springframework.xd.module.ModuleType; /** * Stream/Job deployment state re-calculator upon leadership election. * * @author Patrick Peralta * @author Ilayaperumal Gopinathan */ public class DefaultDeploymentStateRecalculator implements SupervisorElectionListener { /** * Logger. */ private static final Logger logger = LoggerFactory.getLogger(DefaultDeploymentStateRecalculator.class); @Autowired private ZooKeeperConnection zkConnection; /** * Factory to construct {@link org.springframework.xd.dirt.core.Stream} instance */ @Autowired protected StreamFactory streamFactory; /** * Factory to construct {@link org.springframework.xd.dirt.core.Job} instance */ @Autowired protected JobFactory jobFactory; /** * Deployment unit state calculator */ @Autowired protected DeploymentUnitStateCalculator stateCalculator; /** * Iterate all deployed streams, recalculate the state of each, and create * an ephemeral node indicating the stream state. This is typically invoked * upon leader election. * * @throws Exception */ public void recalculateStreamStates(PathChildrenCache streamDeployments) throws Exception { Assert.notNull(streamDeployments, "Stream deployment path cache shouldn't be null."); CuratorFramework client = zkConnection.getClient(); for (Iterator<String> iterator = new ChildPathIterator<String>(ZooKeeperUtils.stripPathConverter, streamDeployments); iterator.hasNext(); ) { String streamName = iterator.next(); String definitionPath = Paths.build(Paths.build(Paths.STREAM_DEPLOYMENTS, streamName)); try { final Stream stream = DeploymentLoader.loadStream(client, streamName, streamFactory); if (stream != null) { String streamModulesPath = Paths.build(definitionPath, Paths.MODULES); List<ModuleDeploymentStatus> statusList = new ArrayList<ModuleDeploymentStatus>(); try { List<String> moduleDeployments = client.getChildren().forPath(streamModulesPath); for (String moduleDeployment : moduleDeployments) { StreamDeploymentsPath streamDeploymentsPath = new StreamDeploymentsPath( Paths.build(streamModulesPath, moduleDeployment)); statusList.add(new ModuleDeploymentStatus( streamDeploymentsPath.getContainer(), streamDeploymentsPath.getModuleSequence(), new ModuleDescriptor.Key(streamName, ModuleType.valueOf(streamDeploymentsPath.getModuleType()), streamDeploymentsPath.getModuleLabel()), ModuleDeploymentStatus.State.deployed, null)); } } catch (KeeperException.NoNodeException e) { // indicates there are no modules deployed for this stream; // ignore as this will result in an empty statusList } writeDeploymentUnitStatus(DeploymentUnitType.Stream, streamName, stateCalculator.calculate(stream, new DefaultModuleDeploymentPropertiesProvider(stream), statusList)); } } catch (Exception e) { logger.error(String.format("Exception calculating status for stream %s; status will be set to %s.", streamName, DeploymentUnitStatus.State.unknown), e); writeDeploymentUnitStatus(DeploymentUnitType.Stream, streamName, new DeploymentUnitStatus(DeploymentUnitStatus.State.unknown)); } } } /** * Iterate all deployed jobs, recalculate the deployment status of each, and * create an ephemeral node indicating the job state. This is typically invoked * upon leader election. * * @throws Exception */ public void recalculateJobStates(PathChildrenCache jobDeployments) throws Exception { Assert.notNull(jobDeployments, "Stream deployment path cache shouldn't be null."); CuratorFramework client = zkConnection.getClient(); for (Iterator<String> iterator = new ChildPathIterator<String>(ZooKeeperUtils.stripPathConverter, jobDeployments); iterator.hasNext();) { String jobName = iterator.next(); try { final Job job = DeploymentLoader.loadJob(client, jobName, jobFactory); if (job != null) { String jobModulesPath = Paths.build(Paths.JOB_DEPLOYMENTS, jobName, Paths.MODULES); List<ModuleDeploymentStatus> statusList = new ArrayList<ModuleDeploymentStatus>(); List<String> moduleDeployments = client.getChildren().forPath(jobModulesPath); for (String moduleDeployment : moduleDeployments) { JobDeploymentsPath jobDeploymentsPath = new JobDeploymentsPath( Paths.build(jobModulesPath, moduleDeployment)); statusList.add(new ModuleDeploymentStatus( jobDeploymentsPath.getContainer(), jobDeploymentsPath.getModuleSequence(), new ModuleDescriptor.Key(jobName, ModuleType.job, jobDeploymentsPath.getModuleLabel()), ModuleDeploymentStatus.State.deployed, null)); } writeDeploymentUnitStatus(DeploymentUnitType.Job, jobName, stateCalculator.calculate(job, new DefaultModuleDeploymentPropertiesProvider(job), statusList)); } } catch (Exception e) { logger.error(String.format("Exception calculating status for job %s; status will be set to %s.", jobName, DeploymentUnitStatus.State.unknown), e); writeDeploymentUnitStatus(DeploymentUnitType.Job, jobName, new DeploymentUnitStatus(DeploymentUnitStatus.State.unknown)); } } } /** * Write the deployment status of the deployment unit. * * @param type deployment unit type * @param name deployment unit name * @param status deployment unit status * @throws Exception */ private void writeDeploymentUnitStatus(DeploymentUnitType type, String name, DeploymentUnitStatus status) throws Exception { CuratorFramework client = zkConnection.getClient(); logger.info("Deployment status for {} '{}': {}", type, name, status); String statusPath = Paths.build( type == DeploymentUnitType.Stream ? Paths.STREAM_DEPLOYMENTS : Paths.JOB_DEPLOYMENTS, name, Paths.STATUS); Stat stat = client.checkExists().forPath(statusPath); if (stat != null) { logger.trace("Found old status path {}; stat: {}", statusPath, stat); client.delete().forPath(statusPath); } client.create().withMode(CreateMode.EPHEMERAL).forPath(statusPath, ZooKeeperUtils.mapToBytes(status.toMap())); } @Override public void onSupervisorElected(SupervisorElectedEvent supervisorElectedEvent) throws Exception { recalculateStreamStates(supervisorElectedEvent.getStreamDeployments()); recalculateJobStates(supervisorElectedEvent.getJobDeployments()); } }