/*
* Copyright (c) 2015 EMC Corporation
* All Rights Reserved
*/
package com.emc.sa.zookeeper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.log4j.Logger;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.emc.sa.descriptor.AbstractServiceDescriptors;
import com.emc.sa.descriptor.ServiceDefinition;
import com.emc.storageos.coordinator.client.service.CoordinatorClient;
import com.emc.storageos.coordinator.client.service.DistributedDataManager;
/**
* Stores service descriptors in Zookeeper nodes.
*
* Descriptors are stored in nodes that have the same name as the serviceid
*/
@Component
public class ZkServiceDescriptors extends AbstractServiceDescriptors {
private static Logger LOG = Logger.getLogger(ZkServiceDescriptors.class);
private static final int MAX_SERVICE_NODES = 200;
private static String ZK_SERVICE_DEFINITION_PATH = "/portal/servicedefinitions";
private CoordinatorClient coordinatorClient;
private DistributedDataManager dataManager;
@PostConstruct
public void start() {
try {
coordinatorClient.start();
dataManager = coordinatorClient.createDistributedDataManager(ZK_SERVICE_DEFINITION_PATH, MAX_SERVICE_NODES);
} catch (IOException e) {
LOG.error(e);
throw new RuntimeException("Error Starting ServiceDescriptors", e);
}
}
@Autowired
public void setCoordinatorClient(CoordinatorClient coordinatorClient) {
this.coordinatorClient = coordinatorClient;
}
/**
* @return A list all service definitions stored in Zookeeper
*/
@Override
protected Collection<ServiceDefinition> getServiceDefinitions() {
List<ServiceDefinition> serviceDefinitions = new ArrayList<>();
try {
if (dataManager.checkExists(ZK_SERVICE_DEFINITION_PATH) != null) {
for (String serviceId : dataManager.getChildren(ZK_SERVICE_DEFINITION_PATH)) {
try {
serviceDefinitions.add(getServiceDefinition(serviceId));
} catch (Exception e) {
LOG.error("Failed to get definition for service: " + serviceId, e);
}
}
}
} catch (Exception e) {
LOG.error("Error listing Service definitions", e);
}
return serviceDefinitions;
}
@Override
protected ServiceDefinition getServiceDefinition(String serviceId) {
try {
String path = getServiceDefinitionPath(serviceId);
if (dataManager.checkExists(path) != null) {
return (ServiceDefinition) dataManager.getData(path, false);
}
} catch (Exception e) {
LOG.error("Error getting service definition: " + serviceId, e);
}
throw new IllegalStateException("Service " + serviceId + " not found");
}
/**
* Adds all the given service definitions to the Zookeeper tree
*/
public void addServices(List<ServiceDefinition> services) throws Exception {
ensurePathExists();
Set<String> remainingDescriptors = new HashSet<>(dataManager.getChildren(ZK_SERVICE_DEFINITION_PATH));
for (ServiceDefinition service : services) {
LOG.debug(String.format("Adding Service %s into ZK", service.serviceId));
String path = getServiceDefinitionPath(service.serviceId);
try {
Stat before = dataManager.checkExists(path);
dataManager.putData(path, service);
Stat after = dataManager.checkExists(path);
nodeUpdated(path, before, after);
// Remove the service from the remaining list
remainingDescriptors.remove(service.serviceId);
} catch (Exception e) {
LOG.error(String.format("Failed to add Service %s into ZK, path: %s", service.serviceId, path), e);
throw e;
}
}
// Remove any remaining descriptors
for (String descriptorName : remainingDescriptors) {
LOG.info(String.format("Removing old Service %s from ZK", descriptorName));
dataManager.removeNode(ZK_SERVICE_DEFINITION_PATH + "/" + descriptorName);
}
}
private String getServiceDefinitionPath(String serviceId) {
return ZK_SERVICE_DEFINITION_PATH + "/" + serviceId;
}
private void ensurePathExists() throws Exception {
if (dataManager.checkExists(ZK_SERVICE_DEFINITION_PATH) == null) {
LOG.info("Creating ZK node: " + ZK_SERVICE_DEFINITION_PATH);
dataManager.createNode(ZK_SERVICE_DEFINITION_PATH, false);
nodeUpdated(ZK_SERVICE_DEFINITION_PATH, null, dataManager.checkExists(ZK_SERVICE_DEFINITION_PATH));
}
}
private void nodeUpdated(String path, Stat before, Stat after) {
if (before == null) {
if (after == null) {
LOG.warn(String.format("Failed to create ZK node: %s", path));
}
else {
LOG.debug(String.format("Created ZK node [%s, created:%s]", path, new Date(after.getCtime())));
}
}
else {
int version = after.getVersion();
long beforeTime = before.getMtime();
long afterTime = after.getMtime();
if (beforeTime == afterTime) {
LOG.warn(String.format("Failed to update ZK node [%s, version:%s, before:%s, after:%s]", path, version,
new Date(before.getMtime()), new Date(after.getMtime())));
}
else {
LOG.debug(String.format("Updated ZK node [%s, version:%s, before:%s, after:%s]", path, version,
new Date(before.getMtime()), new Date(after.getMtime())));
}
}
}
@PreDestroy
public void closeDataMangager() {
if (dataManager != null) {
dataManager.close();
}
}
}