/* * Copyright (c) 2008-2011 EMC Corporation * All Rights Reserved */ package com.emc.storageos.coordinator.service.impl; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Iterator; import java.util.Map.Entry; import java.util.Properties; import org.apache.zookeeper.server.quorum.QuorumPeer.LearnerType; import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer; import org.apache.zookeeper.server.quorum.QuorumPeerConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.coordinator.exceptions.CoordinatorException; import com.emc.storageos.services.util.FileUtils; public class SpringQuorumPeerConfig extends QuorumPeerConfig { private static final Logger log = LoggerFactory.getLogger(SpringQuorumPeerConfig.class); private static final String SERVER_ID_FILE = "myid"; // readonly mode should only be enabled on standby site to support unplanned failover // on the active site it should be disabled otherwise the existing behavior of coordinatorsvc will be changed public static final String READONLY_MODE_ENABLED = "readonlymode.enabled"; private int _id; private Properties _properties; public void setMachineId(int id) { _id = id; } public void setProperties(Properties properties) { _properties = properties; } public int getNumberOfParitipants() { return servers.size() - observers.size(); } public void init() throws ConfigException, IOException { String dataDir = (String) _properties.get("dataDir"); //create zk data directory if it doesn't exist File serverIdDir = new File(dataDir); if (!serverIdDir.exists()) { if (!serverIdDir.mkdirs()) { throw CoordinatorException.fatals .unableToCreateServerIDDirectories(serverIdDir.getAbsolutePath()); } } //create zk server id file if it doesn't exist or not match current server id File serverId = new File(serverIdDir, SERVER_ID_FILE); boolean shouldRecreate = true; if (serverId.exists()) { String s = new String(FileUtils.readDataFromFile(serverId.getAbsolutePath())); int currentId = Integer.parseInt(s); if (currentId == _id) { shouldRecreate = false; } } if (shouldRecreate) { FileWriter writer = new FileWriter(serverId); writer.write(Integer.toString(_id)); writer.close(); } Properties prop = new Properties(); prop.putAll(_properties); // Pre-process the properties to parse and remove server.* key/value // In future, if _properties is still used somewhere else after init, we need to clone it keep original values preprocessQuorumServers(prop); parseProperties(prop); } protected void preprocessQuorumServers(Properties zkProp) throws ConfigException { log.info("Preprocess quorum server from properties before send to ZK"); Iterator<Entry<Object, Object>> iterator = zkProp.entrySet().iterator(); while (iterator.hasNext()) { Entry<Object, Object> entry = iterator.next(); String key = entry.getKey().toString().trim(); if (key.startsWith("server.")) { String value = entry.getValue().toString().trim(); createQuorumServer(key, value); iterator.remove(); } } if (zkProp.containsKey(READONLY_MODE_ENABLED)) { System.setProperty(READONLY_MODE_ENABLED, zkProp.getProperty(READONLY_MODE_ENABLED)); } } /** * This logic is same as ZK 3.4.6 except splitting values with "," instead of ":" */ private void createQuorumServer(String key, String value) throws ConfigException { int dot = key.indexOf('.'); long sid = Long.parseLong(key.substring(dot + 1)); String parts[] = value.split(","); if ((parts.length != 2) && (parts.length != 3) && (parts.length != 4)) { log.error(value + " does not have the form host,port or host,port,port " + " or host,port,port,type"); } InetSocketAddress addr = new InetSocketAddress(parts[0], Integer.parseInt(parts[1])); if (parts.length == 2) { servers.put(Long.valueOf(sid), new QuorumServer(sid, addr)); } else if (parts.length == 3) { InetSocketAddress electionAddr = new InetSocketAddress(parts[0], Integer.parseInt(parts[2])); servers.put(Long.valueOf(sid), new QuorumServer(sid, addr, electionAddr)); } else if (parts.length == 4) { InetSocketAddress electionAddr = new InetSocketAddress(parts[0], Integer.parseInt(parts[2])); LearnerType type = LearnerType.PARTICIPANT; if (parts[3].toLowerCase().equals("observer")) { type = LearnerType.OBSERVER; observers.put(Long.valueOf(sid), new QuorumServer(sid, addr, electionAddr, type)); } else if (parts[3].toLowerCase().equals("participant")) { type = LearnerType.PARTICIPANT; servers.put(Long.valueOf(sid), new QuorumServer(sid, addr, electionAddr, type)); } else { throw new ConfigException("Unrecognised peertype: " + value); } } } /** * Create a new config based on current properties and new properties * * @param newProp * @param serverId * @return * @throws IOException * @throws ConfigException */ public SpringQuorumPeerConfig createNewConfig(Properties newProp, int serverId) throws IOException, ConfigException { SpringQuorumPeerConfig newConfig = new SpringQuorumPeerConfig(); newConfig.setMachineId(serverId); Properties prop = new Properties(); prop.putAll(_properties); prop.putAll(newProp); newConfig.setProperties(prop); newConfig.init(); return newConfig; } }