/* * Copyright 2014 University of Southern California * * 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 edu.usc.pgroup.floe.container; import edu.usc.pgroup.floe.config.ConfigProperties; import edu.usc.pgroup.floe.config.FloeConfig; import edu.usc.pgroup.floe.flake.FlakeInfo; import edu.usc.pgroup.floe.resourcemanager.ResourceMapping; import edu.usc.pgroup.floe.signals.ContainerSignal; import edu.usc.pgroup.floe.utils.Utils; import edu.usc.pgroup.floe.zookeeper.ZKClient; import edu.usc.pgroup.floe.zookeeper.ZKUtils; import org.apache.curator.framework.recipes.barriers.DistributedDoubleBarrier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; import java.util.Map; import java.util.Timer; /** * @author kumbhare */ public final class Container { /** * the global logger instance. */ private static final Logger LOGGER = LoggerFactory.getLogger(Container.class); /** * Recurring timer for sending heartbeats. */ private Timer heartBeatTimer; /** * Apps assignment monitor. */ //private AppsAssignmentMonitor appsAssignmentMonitor; /** * Signal monitor. */ private SignalMonitor signalMonitor; /** * Flake monitor. */ private FlakeMonitor flakeMonitor; /** * singleton container instance. */ private static Container instance; /** * Hiding default constructor. */ private Container() { } /** * @return the singleton container instance. */ public static synchronized Container getInstance() { if (instance == null) { instance = new Container(); } return instance; } /** * Starts the server and the schedules the heartbeats. */ public void start() { LOGGER.info("Initializing Container."); initializeContainer(); LOGGER.info("Scheduling container heartbeat."); scheduleHeartBeat(new ContainerHeartbeatTask()); LOGGER.info("Starting application resource mapping monitor"); startResourceMappingMonitor(); LOGGER.info("Starting Signal Monitor."); startSignalMonitor(); LOGGER.info("Starting flake Monitor"); LOGGER.info("Flake port range start: {}", FloeConfig.getConfig().getInt( ConfigProperties.FLAKE_RECEIVER_PORT )); startFlakeMonitor(); LOGGER.info("Container Started."); } /** * Starts the flake monitor, listening for flake heartbeats. */ private void startFlakeMonitor() { flakeMonitor = FlakeMonitor.getInstance(); flakeMonitor.initialize(ContainerInfo.getInstance().getContainerId()); flakeMonitor.startMonitor(); } /** * Starts the curator cache for monitoring the signals. */ private void startSignalMonitor() { signalMonitor = new SignalMonitor(ContainerInfo .getInstance().getContainerId()); } /** * Starts the curator cache for monitoring the applications, * and the pellets assigned to this container. */ private void startResourceMappingMonitor() { //appsAssignmentMonitor = new AppsAssignmentMonitor(ContainerInfo // .getInstance().getContainerId()); } /** * Schedules and starts recurring the container heartbeat. * * @param containerHeartbeatTask Heartbeat timer task. */ private void scheduleHeartBeat(final ContainerHeartbeatTask containerHeartbeatTask) { if (heartBeatTimer == null) { heartBeatTimer = new Timer(); heartBeatTimer.scheduleAtFixedRate(containerHeartbeatTask , 0 , FloeConfig.getConfig().getInt(ConfigProperties .CONTAINER_HEARTBEAT_PERIOD) * Utils.Constants.MILLI); LOGGER.info("Heartbeat scheduled with period: " + FloeConfig.getConfig().getInt( ConfigProperties.CONTAINER_HEARTBEAT_PERIOD) + " seconds"); } } /** * Initializes the container. Including: * setup the containerInfo object (for heartbeat) * TODO: PerfInfoObject etc. */ private void initializeContainer() { ContainerInfo.getInstance().setStartTime(new Date().getTime()); } /** * Processes the signal received (from coordinator or other containers). * @param signal the received signal to process. */ public void processSignal(final ContainerSignal signal) { switch (signal.getSignalType()) { case CREATE_FLAKES: try { createFlakes(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while creating flakes. Will " + "abort"); } break; case INITIALIZE_FLAKES: try { initializeFlakes(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while Initializing flakes. " + " Will abort"); } break; case TERMINATE_FLAKES: try { terminateFlakes(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while launching pellets. Will " + "abort"); } break; case CONNECT_OR_DISCONNECT_FLAKES: try { connectFlakes(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while creating flakes. Will " + "abort"); } break; case INCREASE_OR_DECREASE_PELLETS: try { increaseOrDecreasePellets(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while launching pellets. Will " + "abort"); } break; case START_PELLETS: try { startPellets(signal); } catch (Exception e) { e.printStackTrace(); LOGGER.error("Error occurred while launching pellets. Will " + "abort"); } break; default: LOGGER.info("Unknown Signal type. Ignoring it."); } } /** * creates or terminates pellet instances within a flake. * @param signal The signal (with associated data) received. * @throws Exception If an error occurs while performing the action. */ private void increaseOrDecreasePellets(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); ResourceMapping resourceMapping = ZKUtils.getResourceMapping(appName); String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); if (ContainerActions.isContainerUpdated(resourceMapping)) { barrier.enter(); try { ContainerActions.increaseOrDecreasePellets(resourceMapping); } catch (Exception e) { LOGGER.error("Could not launch pellets. Exception {}", e); } finally { barrier.leave(); } } } /** * creates flakes on the container. * @param signal The signal (with associated data) received. * @throws Exception If an error occurs while performing the action. */ private void connectFlakes(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); ResourceMapping resourceMapping = ZKUtils.getResourceMapping(appName); String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); if (ContainerActions.isContainerUpdated(resourceMapping)) { barrier.enter(); try { ContainerActions.updateFlakeConnections(resourceMapping); } catch (Exception e) { LOGGER.error("Could not create flakes. Exception {}", e); } finally { barrier.leave(); } } } /** * terminates required flakes from the container. (assuming all the pellets * are already terminated). * @param signal The signal (with associated data) received. * @throws Exception If an error occurs while performing the action. */ private void terminateFlakes(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); ResourceMapping resourceMapping = ZKUtils.getResourceMapping(appName); String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); if (ContainerActions.isContainerUpdated(resourceMapping)) { barrier.enter(); try { ContainerActions.terminateFlakes(resourceMapping); } catch (Exception e) { LOGGER.error("Could not terminate flakes. Exception {}", e); } finally { barrier.leave(); } } } /** * initializes newly created flakes on the container. * @param signal The signal (with associated data) received. * @throws Exception If an error occurs while performing the action. */ private void initializeFlakes(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); ResourceMapping resourceMapping = ZKUtils.getResourceMapping(appName); String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); if (ContainerActions.isContainerUpdated(resourceMapping)) { barrier.enter(); try { ContainerActions.initializeFlakes(resourceMapping); } catch (Exception e) { LOGGER.error("Could not create flakes. Exception {}", e); } finally { barrier.leave(); } } } /** * creates new flakes on the container. * @param signal The signal (with associated data) received. * @throws Exception If an error occurs while performing the action. */ private void createFlakes(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); ResourceMapping resourceMapping = ZKUtils.getResourceMapping(appName); String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); if (ContainerActions.isContainerUpdated(resourceMapping)) { barrier.enter(); try { ContainerActions.createFlakes(resourceMapping); } catch (Exception e) { LOGGER.error("Could not create flakes. Exception {}", e); } finally { barrier.leave(); } } } /** * Sends signal to flakes to start all pellets. * mapping. * @param signal The signal (with associated data) received. * @exception Exception if an error occurs while launching flakes. */ private void startPellets(final ContainerSignal signal) throws Exception { String appName = signal.getDestApp(); String containerName = signal.getDestContainer(); //ignore. byte[] data = signal.getSignalData(); LOGGER.info("Container Id: " + containerName); String resourceMappingPath = ZKUtils .getApplicationResourceMapPath(appName); byte[] serializedRM = null; try { serializedRM = ZKClient.getInstance().getCuratorClient().getData() .forPath(resourceMappingPath); } catch (Exception e) { LOGGER.error("Could not receive resource mapping. Aborting."); return; } ResourceMapping resourceMapping = (ResourceMapping) Utils.deserialize(serializedRM); String containerId = ContainerInfo.getInstance().getContainerId(); ResourceMapping.ContainerInstance container = resourceMapping.getContainer(containerId); if (container == null) { LOGGER.info("No resource mapping for this container."); return; } String appUpdateBarrierPath = ZKUtils .getApplicationBarrierPath(appName); int numContainersToUpdate = resourceMapping.getContainersToUpdate(); DistributedDoubleBarrier barrier = new DistributedDoubleBarrier( ZKClient.getInstance().getCuratorClient(), appUpdateBarrierPath, numContainersToUpdate + 1 ); LOGGER.info("Entering barrier: " + appUpdateBarrierPath); barrier.enter(); //start processing. LOGGER.info("Starting pellets."); Map<String, ResourceMapping.FlakeInstance> flakes = container.getFlakes(); Map<String, FlakeInfo> pidToFidMap = FlakeMonitor.getInstance().getFlakes(); for (Map.Entry<String, ResourceMapping.FlakeInstance> flakeEntry : flakes.entrySet()) { String pid = flakeEntry.getKey(); ContainerUtils.sendStartPelletsCommand( pidToFidMap.get(pid).getFlakeId()); } barrier.leave(); //finish launching pellets. } }