diff --git slider-core/src/main/java/org/apache/slider/server/appmaster/ServiceTimelinePublisher.java slider-core/src/main/java/org/apache/slider/server/appmaster/ServiceTimelinePublisher.java new file mode 100644 index 0000000..11e1e9b --- /dev/null +++ slider-core/src/main/java/org/apache/slider/server/appmaster/ServiceTimelinePublisher.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.slider.server.appmaster; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; +import org.apache.hadoop.yarn.api.records.ContainerId; +import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; +import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntity; +import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEvent; +import org.apache.hadoop.yarn.client.api.TimelineClient; +import org.apache.slider.api.ClusterDescription; +import org.apache.slider.api.InternalKeys; +import org.apache.slider.api.OptionKeys; +import org.apache.slider.core.exceptions.BadConfigException; +import org.apache.slider.core.exceptions.SliderException; +import org.apache.slider.server.appmaster.state.AppState; +import org.apache.slider.server.appmaster.state.RoleInstance; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; + +public class ServiceTimelinePublisher { + + protected static final Logger log = + LoggerFactory.getLogger(ServiceTimelinePublisher.class); + + @InterfaceAudience.Private + public enum SliderTimelineEvent { + SL_START, SL_END, SL_CONTAINER_START, SL_CONTAINER_END, + SL_COMPONENT_REGISTERED, SL_COMPONENT_UNREGISTERED + } + + @InterfaceAudience.Private + public enum SliderTimelineEntityType { + SL_APPLICATION, SL_CONTAINER, SL_COMPONENT + } + + public static void publishAppStartEvent(TimelineClient client, + ApplicationAttemptId attemptId, AppState state) + throws SliderException { + TimelineEntity entity = new TimelineEntity(); + entity.setId(attemptId.toString()); + entity.setType(SliderTimelineEntityType.SL_APPLICATION.toString()); + long ts = System.currentTimeMillis(); + entity.setCreatedTime(ts); + + ClusterDescription cd = state.getClusterStatus(); + entity.addInfo(InternalKeys.INTERNAL_DATA_DIR_PATH, cd.dataPath); + entity.addInfo(OptionKeys.APPLICATION_NAME, cd.name); + entity.addInfo(InternalKeys.INTERNAL_SNAPSHOT_CONF_PATH, + cd.originConfigurationPath); + entity.addInfo(InternalKeys.INTERNAL_GENERATED_CONF_PATH, + cd.generatedConfigurationPath); + entity.addInfo(InternalKeys.INTERNAL_APPLICATION_IMAGE_PATH, + cd.getImagePath()); + entity.addInfo(InternalKeys.INTERNAL_APPLICATION_HOME, + cd.getApplicationHome()); + entity.addInfo(OptionKeys.ZOOKEEPER_PATH, cd.getZkPath()); + entity.addInfo(OptionKeys.ZOOKEEPER_QUORUM, cd.getZkHosts()); + + + TimelineEvent startEvent = new TimelineEvent(); + startEvent.setId(SliderTimelineEvent.SL_START.toString()); + startEvent.setTimestamp(ts); + entity.addEvent(startEvent); + postEntities(client, "App Attempt start event could not be published for " + + attemptId, entity); + } + + public static void publishAppFinishedEvent(TimelineClient client, + ApplicationAttemptId attemptId, int exitCode, + FinalApplicationStatus finalStatus, AppState state) { + TimelineEntity entity = new TimelineEntity(); + entity.setId(attemptId.toString()); + entity.setType(SliderTimelineEntityType.SL_APPLICATION.toString()); + entity.addInfo("ExitCode", exitCode); + // TODO: add more info + TimelineEvent startEvent = new TimelineEvent(); + startEvent.setId(SliderTimelineEvent.SL_END.toString()); + startEvent.setTimestamp(System.currentTimeMillis()); + entity.addEvent(startEvent); + postEntities(client, "App Attempt end event could not be published for " + + attemptId, entity); + } + + public static void publishComponentRegisterEvent(TimelineClient client, + ContainerId containerId, String description, AppState state) { + RoleInstance instance = state.getOwnedContainer(containerId); + TimelineEntity entity = new TimelineEntity(); + entity.setId(containerId.toString()); + entity.setType(SliderTimelineEntityType.SL_CONTAINER.toString()); + long ts = System.currentTimeMillis(); + entity.setCreatedTime(ts); + entity.addInfo("description", description); + entity.addInfo("role", instance.role); + entity.addInfo("roleId", instance.roleId); + entity.addInfo("command", instance.command); + entity.addInfo("environment", Arrays.toString(instance.environment)); + // TODO: add more info + TimelineEvent registrationEvent = new TimelineEvent(); + registrationEvent.setId( + SliderTimelineEvent.SL_COMPONENT_REGISTERED.toString()); + registrationEvent.setTimestamp(ts); + entity.addEvent(registrationEvent); + postEntities(client, + "Component registered event could not be published for " + + containerId.toString(), entity); + } + + public static void publishComponentUnregisterEvent(TimelineClient client, + ContainerId containerId, AppState state) { + TimelineEntity entity = new TimelineEntity(); + entity.setId(containerId.toString()); + entity.setType(SliderTimelineEntityType.SL_CONTAINER.toString()); + // TODO: add more info + TimelineEvent unregistrationEvent = new TimelineEvent(); + unregistrationEvent.setId( + SliderTimelineEvent.SL_COMPONENT_UNREGISTERED.toString()); + unregistrationEvent.setTimestamp(System.currentTimeMillis()); + entity.addEvent(unregistrationEvent); + postEntities(client, + "Component unregistered event could not be published for " + + containerId.toString(), entity); + } + + private static void postEntities(TimelineClient client, + String exceptionMessage, TimelineEntity... entities) { + try { + client.putEntitiesAsync(entities); + } catch (Exception e) { + log.error(exceptionMessage, + e instanceof UndeclaredThrowableException ? e.getCause() : e); + } + } +} diff --git slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java index 040f12b..b58db45 100644 --- slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java +++ slider-core/src/main/java/org/apache/slider/server/appmaster/SliderAppMaster.java @@ -62,6 +62,7 @@ import org.apache.hadoop.yarn.api.records.NodeState; import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.client.api.AMRMClient; +import org.apache.hadoop.yarn.client.api.TimelineClient; import org.apache.hadoop.yarn.client.api.async.AMRMClientAsync; import org.apache.hadoop.yarn.client.api.async.NMClientAsync; import org.apache.hadoop.yarn.client.api.async.impl.NMClientAsyncImpl; @@ -250,6 +251,11 @@ public class SliderAppMaster extends AbstractSliderLaunchedService @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private AMRMClientAsync asyncRMClient; + /** Handle to communicate with the timeline service*/ + private TimelineClient timelineClient; + + private boolean timelineServiceEnabled = false; + @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized") private RMOperationHandler rmOperationHandler; @@ -500,6 +506,11 @@ public class SliderAppMaster extends AbstractSliderLaunchedService new ServiceThreadFactory("AmExecutor", true))); addService(executorService); + if (YarnConfiguration.timelineServiceV2Enabled(conf)) { + timelineServiceEnabled = true; + log.info("Enabled YARN timeline service v2. "); + } + addService(actionQueues); //init all child services @@ -720,6 +731,16 @@ public class SliderAppMaster extends AbstractSliderLaunchedService //now bring it up deployChildService(asyncRMClient); + // create and add timeline client if timeline service is enabled in the + // cluster + if (timelineServiceEnabled) { + timelineClient = TimelineClient.createTimelineClient(appid); + timelineClient.init(getConfig()); + timelineClient.start(); + asyncRMClient.registerTimelineClient(timelineClient); + log.debug("Timeline client registered. "); + } + addIfService(timelineClient); // nmclient relays callbacks back to this class nmClientAsync = new NMClientAsyncImpl("nmclient", this); @@ -975,6 +996,11 @@ public class SliderAppMaster extends AbstractSliderLaunchedService // declare the cluster initialized log.info("Application Master Initialization Completed"); initCompleted.set(true); + // post timeline data to YARN + if (timelineServiceEnabled) { + ServiceTimelinePublisher.publishAppStartEvent(timelineClient, + appAttemptID, appState); + } scheduleFailureWindowResets(instanceDefinition.getResources()); scheduleEscalation(instanceDefinition.getInternal()); @@ -1400,6 +1426,10 @@ public class SliderAppMaster extends AbstractSliderLaunchedService id, description, e, e); return false; } + if (timelineServiceEnabled) { + ServiceTimelinePublisher.publishComponentRegisterEvent(timelineClient, + id, description, appState); + } return true; } @@ -1436,6 +1466,10 @@ public class SliderAppMaster extends AbstractSliderLaunchedService } catch (IOException e) { log.warn("Failed to delete container {} : {}", id, e, e); } + if (timelineServiceEnabled) { + ServiceTimelinePublisher.publishComponentUnregisterEvent(timelineClient, + id, appState); + } } /** @@ -1580,6 +1614,12 @@ public class SliderAppMaster extends AbstractSliderLaunchedService log.debug("Stopped forked process: exit code={}", forkedExitCode); } + // Publish finish event to timeline v2 + if (timelineServiceEnabled) { + ServiceTimelinePublisher.publishAppFinishedEvent(timelineClient, + appAttemptID, exitCode, appStatus, this.appState); + } + // make sure the AM is actually registered. If not, there's no point // trying to unregister it if (amRegistrationData == null) {