/*
* 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.client.FloeClient;
import edu.usc.pgroup.floe.flake.FlakeInfo;
import edu.usc.pgroup.floe.resourcemanager.ResourceMapping;
import edu.usc.pgroup.floe.resourcemanager.ResourceMappingDelta;
import edu.usc.pgroup.floe.thriftgen.TPellet;
import edu.usc.pgroup.floe.utils.RetryLoop;
import edu.usc.pgroup.floe.utils.RetryPolicyFactory;
import edu.usc.pgroup.floe.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* @author kumbhare
*/
public final class ContainerActions {
/**
* the global logger instance.
*/
private static final Logger LOGGER =
LoggerFactory.getLogger(ContainerActions.class);
/**
* Hiding default constructor.
*/
private ContainerActions() {
}
/**
* Function to initialize flakes within the container from the resource
* mapping.
* @param mapping Current Resource mapping
* @throws Exception if there is an error communicating with ZK or while
* launching flakes.
*/
public static void initializeFlakes(final ResourceMapping mapping)
throws Exception {
String appName = mapping.getAppName();
String containerId = ContainerInfo.getInstance().getContainerId();
Map<String, ResourceMapping.FlakeInstance> flakes = null;
if (mapping.getDelta() == null) {
ResourceMapping.ContainerInstance container
= mapping.getContainer(containerId);
if (container != null) {
flakes = container.getFlakes();
} else {
LOGGER.warn("No flakes for this container.");
}
} else {
Map<String, ResourceMappingDelta.FlakeInstanceDelta>
addedflakeDeltas
= mapping.getDelta().getNewlyAddedFlakes(containerId);
if (addedflakeDeltas != null) {
flakes = new HashMap<>();
for (Map.Entry<String, ResourceMappingDelta.FlakeInstanceDelta>
fd : addedflakeDeltas.entrySet()) {
flakes.put(fd.getKey(), fd.getValue().getFlakeInstance());
}
} else {
LOGGER.warn("No new flakes for this container.");
}
}
//Create and wait for flakes to respond.
if (flakes != null) {
ContainerUtils.initializeFlakes(flakes);
}
}
/**
* Function to create flakes within the container from the resource mapping.
* @param mapping Current Resource mapping
* @throws Exception if there is an error communicating with ZK or while
* launching flakes.
*/
public static void createFlakes(final ResourceMapping mapping)
throws Exception {
String appName = mapping.getAppName();
String applicationJar = mapping.getApplicationJarPath();
if (applicationJar != null) {
try {
String downloadLocation
= Utils.getContainerJarDownloadPath(appName,
applicationJar);
File f = new File(downloadLocation);
if (!f.exists()) {
LOGGER.info("Downloading: " + applicationJar);
FloeClient.getInstance().downloadFileSync(applicationJar,
downloadLocation);
LOGGER.info("Finished Downloading: " + applicationJar);
}
} catch (Exception e) {
LOGGER.warn("No application jar specified. It should work"
+ " still work for inproc testing. Exception: {}",
e);
}
}
String containerId = ContainerInfo.getInstance().getContainerId();
Map<String, ResourceMapping.FlakeInstance> flakes = null;
if (mapping.getDelta() == null) {
ResourceMapping.ContainerInstance container
= mapping.getContainer(containerId);
if (container != null) {
flakes = container.getFlakes();
} else {
LOGGER.warn("No flakes for this container.");
}
} else {
Map<String, ResourceMappingDelta.FlakeInstanceDelta>
addedflakeDeltas
= mapping.getDelta().getNewlyAddedFlakes(containerId);
if (addedflakeDeltas != null) {
flakes = new HashMap<>();
for (Map.Entry<String, ResourceMappingDelta.FlakeInstanceDelta>
fd : addedflakeDeltas.entrySet()) {
flakes.put(fd.getKey(), fd.getValue().getFlakeInstance());
}
} else {
LOGGER.warn("No new flakes for this container.");
}
}
//Create and wait for flakes to respond.
if (flakes != null) {
ContainerUtils.createFlakes(
mapping.getAppName(),
mapping.getApplicationJarPath(),
containerId,
flakes);
}
}
/**
* Function to terminate flakes if any as determined by the Resource
* Mapping. Do not blindly terminate all flakes in the container for the
* given app.
* Precondition: Flake should not have any
* pellets running.
* @param mapping Current Resource mapping
* @throws Exception if there is an error communicating with ZK or while
* launching flakes.
*/
public static void terminateFlakes(final ResourceMapping mapping)
throws Exception {
String containerId = ContainerInfo.getInstance().getContainerId();
if (mapping.getDelta() != null) {
Map<String,
ResourceMappingDelta.FlakeInstanceDelta> flakeDeltasRemoved
= mapping.getDelta().getRemovedFlakes(containerId);
if (flakeDeltasRemoved != null && flakeDeltasRemoved.size() > 0) {
ContainerUtils.terminateFlakes(mapping, flakeDeltasRemoved);
}
}
}
/**
* Function to increment or decrement the pellets for certain flakes based
* on the resource mapping.
* @param mapping Current Resource mapping.
* @throws Exception if there is an error communicating with ZK or while
* add/removing pellets.
*/
public static void increaseOrDecreasePellets(
final ResourceMapping mapping) throws Exception {
String containerId = ContainerInfo.getInstance().getContainerId();
Map<String, ResourceMapping.FlakeInstance> flakes = null;
Map<String, Integer> pelletsToIncrOrDecr = new HashMap<>();
if (mapping.getDelta() == null) {
ResourceMapping.ContainerInstance container
= mapping.getContainer(containerId);
if (container != null) {
flakes = container.getFlakes();
} else {
LOGGER.warn("No pellets for this container.");
}
for (Map.Entry<String, ResourceMapping.FlakeInstance> flake
: flakes.entrySet()) {
pelletsToIncrOrDecr.put(flake.getKey(),
flake.getValue().getNumPelletInstances());
}
} else {
Map<String, ResourceMappingDelta.FlakeInstanceDelta>
modifiedFlakes = new HashMap<>();
if (mapping.getDelta().getNewlyAddedFlakes(containerId) != null) {
modifiedFlakes.putAll(
mapping.getDelta().getNewlyAddedFlakes(containerId));
}
if (mapping.getDelta().getUpdatedFlakes(containerId) != null) {
modifiedFlakes.putAll(
mapping.getDelta().getUpdatedFlakes(containerId));
}
// if (mapping.getDelta().getRemovedFlakes(containerId) != null) {
// modifiedFlakes.putAll(
// mapping.getDelta().getRemovedFlakes(containerId));
// }
if (modifiedFlakes != null && modifiedFlakes.size() > 0) {
flakes = new HashMap<>();
for (Map.Entry<String, ResourceMappingDelta.FlakeInstanceDelta>
fd : modifiedFlakes.entrySet()) {
flakes.put(fd.getKey(), fd.getValue().getFlakeInstance());
int diffPellets = fd.getValue().getNumInstancesAdded()
- fd.getValue().getNumInstancesRemoved();
LOGGER.info("Flake to update: INCR/DECR pellets by {}, "
+ "{}, {}",
fd.getValue().getNumInstancesAdded(),
fd.getValue().getNumInstancesRemoved(),
diffPellets);
pelletsToIncrOrDecr.put(fd.getKey(), diffPellets);
}
} else {
LOGGER.warn("No new pellets for this container.");
}
}
try {
if (flakes != null) {
for (Map.Entry<String, ResourceMapping.FlakeInstance> flakeEntry
: flakes.entrySet()) {
final String pid = flakeEntry.getKey();
FlakeInfo info = RetryLoop.callWithRetry(RetryPolicyFactory
.getDefaultPolicy(),
new Callable<FlakeInfo>() {
@Override
public FlakeInfo call() throws Exception {
return FlakeMonitor.getInstance()
.getFlakeInfo(pid);
}
});
LOGGER.info("Found Flake:{}", info.getFlakeId());
ResourceMapping.FlakeInstance flakeInstance
= flakeEntry.getValue();
TPellet pellet = mapping.getFloeApp().get_pellets()
.get(flakeInstance.getCorrespondingPelletId());
byte[] activeAlternate = pellet.get_alternates().get(
pellet.get_activeAlternate()
).get_serializedPellet();
int diffPellets = pelletsToIncrOrDecr.get(pid);
LOGGER.info("Creating {} instances.", diffPellets);
if (diffPellets > 0) {
//TO INCREMENT.
LOGGER.info("Incrementing pellet instances for flake: "
+ "{} by {}", info.getFlakeId(), diffPellets);
for (int i = 0; i < diffPellets; i++) {
ContainerUtils.sendIncrementPelletCommand(
info.getFlakeId(),
activeAlternate
);
}
} else if (diffPellets < 0) {
//TO Decrement.
diffPellets = Math.abs(diffPellets);
LOGGER.info("Decrementing pellet instances for flake: "
+ "{} by {}", info.getFlakeId(), diffPellets);
for (int i = 0; i < diffPellets; i++) {
ContainerUtils.sendDecrementPelletCommand(
info.getFlakeId()
);
}
}
}
}
} catch (Exception ex) {
LOGGER.error("Could not update flake. {}", ex);
}
}
/**
* Function to increment or decrement the pellets for certain flakes based
* on the resource mapping.
* @param mapping Current Resource mapping.
* @throws Exception if there is an error communicating with ZK or while
* add/removing pellets.
*/
public static void updateFlakeConnections(
final ResourceMapping mapping) throws Exception {
//Two things to consider.
//First.. For new flakes. Connect to ALL predecessors.
//Second.. For all flakes, Connect to NEW predecessor Flakes.
//First.. new flakes.
String containerId = ContainerInfo.getInstance().getContainerId();
Map<String, ResourceMapping.FlakeInstance> flakes = null;
if (mapping.getDelta() == null) {
ResourceMapping.ContainerInstance container
= mapping.getContainer(containerId);
if (container != null) {
flakes = container.getFlakes();
} else {
LOGGER.warn("No flakes for this container.");
}
} else {
Map<String, ResourceMappingDelta.FlakeInstanceDelta>
addedflakeDeltas
= mapping.getDelta().getNewlyAddedFlakes(containerId);
if (addedflakeDeltas != null) {
flakes = new HashMap<>();
for (Map.Entry<String, ResourceMappingDelta.FlakeInstanceDelta>
fd : addedflakeDeltas.entrySet()) {
flakes.put(fd.getKey(), fd.getValue().getFlakeInstance());
}
} else {
LOGGER.warn("No new flakes for this container.");
}
}
if (flakes != null) {
Map<String, FlakeInfo> pidToFidMap
= FlakeMonitor.getInstance().getFlakes();
for (Map.Entry<String, ResourceMapping.FlakeInstance> flakeEntry
: flakes.entrySet()) {
String pid = flakeEntry.getKey();
List<ResourceMapping.FlakeInstance> preds
= mapping
.getPrecedingFlakes(pid);
LOGGER.info("ALL PREDS: {}", preds);
for (ResourceMapping.FlakeInstance pred : preds) {
int assignedPort = pred.getAssignedPort(pid);
int backPort = pred.getAssignedBackPort(pid);
String host = pred.getHost();
LOGGER.info("Connecting to pred: {}", pred.getHost());
ContainerUtils.sendConnectCommand(
pidToFidMap.get(pid).getFlakeId(),
host, assignedPort, backPort);
}
}
}
//Second. For all existing flakes, connect to new predecessors.
Map<String, FlakeInfo> runningFlakes
= FlakeMonitor.getInstance().getFlakes();
for (Map.Entry<String, FlakeInfo> entry
: runningFlakes.entrySet()) {
//THIS IS REQUIRED ONLY IF THIS IS NOT A NEWLY CREATED PELLET
// . BECAUSE THE launchAndInitialize function will take care
// of connections.
//SO CHECK IF THIS PELLET IS PRESENT IN THE NEWLY ADDED LIST.
if (flakes != null
&& flakes.containsKey(entry.getKey())) {
continue;
}
String pelletName = entry.getKey();
FlakeInfo flakeInfo = entry.getValue();
//get DELTA ONLY those predecessor flakes that have been
// added for this pellet.
List<ResourceMappingDelta.FlakeInstanceDelta> newPredFlakes
= mapping.getDelta()
.getNewlyAddedPrecedingFlakes(pelletName);
if (newPredFlakes != null) {
for (ResourceMappingDelta.FlakeInstanceDelta predDelta
: newPredFlakes) {
ResourceMapping.FlakeInstance pred
= predDelta.getFlakeInstance();
int assignedPort = pred.getAssignedPort(pelletName);
int backPort = pred.getAssignedBackPort(pelletName);
String host = pred.getHost();
ContainerUtils.sendConnectCommand(
flakeInfo.getFlakeId(),
host, assignedPort, backPort);
}
} else {
LOGGER.info("No new pred. flakes. So no new connections.");
}
}
}
/**
* Utility function to check if the current container has any updates to
* make.
* @param mapping Current Resource mapping.
* @return true if updates are required, false other wise.
*/
public static boolean isContainerUpdated(final ResourceMapping mapping) {
String containerId = ContainerInfo.getInstance().getContainerId();
ResourceMapping.ContainerInstance container
= mapping.getContainer(containerId);
if (container == null) {
LOGGER.warn("No flakes for this container.");
return false;
} else {
return true;
}
}
}