/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.ipreconfig; import com.emc.storageos.model.property.PropertyConstants; import com.emc.storageos.services.util.Exec; import com.emc.storageos.services.util.FileUtils; import com.emc.storageos.services.util.PlatformUtils; import com.emc.storageos.systemservices.impl.ipreconfig.IpReconfigConstants; import com.emc.storageos.systemservices.impl.ipreconfig.IpReconfigUtil; import com.emc.vipr.model.sys.ipreconfig.ClusterIpInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.lang.Exception; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; /** * Ip reconfiguration tool which could * 1. Commit new IPs to the ovfenv partition * 2. Rollback to original IPs to the ovfenv partition */ public class Main { private static final Logger log = LoggerFactory.getLogger(Main.class); private static final int CMD_TIMEOUT = 10 * 1000; private static void usage() { System.out.println("Usage: "); System.out.println("ipreconfig [commit|rollback]"); System.exit(-1); } public static void main(String[] args) throws Exception { if (args.length != 1) { usage(); } try { if (args[0].equals("commit")) { commitNewIP(); } else if (args[0].equals("rollback")) { rollbackToOldIP(); } else { usage(); } } catch (Exception e) { log.error(e.getMessage(), e); } } private static void commitNewIP() throws Exception { if (!FileUtils.exists(IpReconfigConstants.NEWIP_PATH)) { log.info("No new ip file {} for applying. Exit...", IpReconfigConstants.NEWIP_PATH); return; } String strExpirationTime = (String) FileUtils.readObjectFromFile(IpReconfigConstants.NEWIP_EXPIRATION); if (System.currentTimeMillis() >= Long.valueOf(strExpirationTime)) { log.info("Ip reconfiguration procedure has expired. Will not applying new IPs info. Exit..."); return; } // 1. get current ip info from ovfenv device Map<String, String> ovfenvprop = PlatformUtils.getOvfenvProperties(); String node_id = ovfenvprop.get(PropertyConstants.NODE_ID_KEY); int node_count = Integer.valueOf(ovfenvprop.get(PropertyConstants.NODE_COUNT_KEY)); // 2. get new ip info from local file ClusterIpInfo newIpinfo = IpReconfigUtil.readIpinfoFile(IpReconfigConstants.NEWIP_PATH); // 3. load node status from local file IpReconfigConstants.NodeStatus localnode_status = IpReconfigConstants.NodeStatus.valueOf(IpReconfigUtil.readNodeStatusFile()); log.info("Local node's status is {}.", localnode_status); switch (localnode_status) { case None: case LOCALAWARE_LOCALPERSISTENT: log.info("NewIP exists, but should not be commited. Exiting...", localnode_status); return; case LOCALAWARE_CLUSTERPERSISTENT: log.info("Trying to connect with new cluster ...", localnode_status); handlePossibleJump(newIpinfo, node_id, node_count); break; case CLUSTERACK_CLUSTERPERSISTENT: log.info("Trying to commit new IPs directly ...", localnode_status); applyIpinfo(newIpinfo, node_id, node_count, true); break; default: log.info("No need to handle status {}...", localnode_status); return; } } private static void rollbackToOldIP() throws Exception { if (!FileUtils.exists(IpReconfigConstants.OLDIP_PATH)) { log.info("No old ip file {} for rollback. Exit...", IpReconfigConstants.OLDIP_PATH); return; } // 1. get current ip info from ovfenv device Map<String, String> ovfenvprop = PlatformUtils.getOvfenvProperties(); String node_id = ovfenvprop.get(PropertyConstants.NODE_ID_KEY); int node_count = Integer.valueOf(ovfenvprop.get(PropertyConstants.NODE_COUNT_KEY)); // 2. get old ip info from local file ClusterIpInfo oldIpinfo = IpReconfigUtil.readIpinfoFile(IpReconfigConstants.OLDIP_PATH); // 3. apply old ipinfo applyIpinfo(oldIpinfo, node_id, node_count, false); } private static void applyIpinfo(ClusterIpInfo ipinfo, String nodeid, int node_count, boolean bNewIp) throws Exception { log.info("applying ip info: {}", ipinfo.toString()); String isoFilePath = "/tmp/ovf-env.iso"; File isoFile = new File(isoFilePath); try { String tmpstr = PlatformUtils.genOvfenvPropertyKVString(ipinfo, nodeid, node_count); PlatformUtils.genOvfenvIsoImage(tmpstr, isoFilePath); String ovfenv_part = PlatformUtils.probeOvfenvPartition(); Path path = Paths.get(isoFilePath); byte[] data = Files.readAllBytes(path); FileUtils.writePlainFile(ovfenv_part, data); log.info("Succeed to apply the ip info : {} to ovfenv partition", ipinfo.toString()); } catch (Exception e) { log.error("Applying ip info to ovfenv partition failed with exception: {}", e.getMessage()); } finally { isoFile.delete(); } if (bNewIp) { FileUtils.deleteFile(IpReconfigConstants.NEWIP_PATH); FileUtils.deleteFile(IpReconfigConstants.NEWIP_EXPIRATION); } else { IpReconfigUtil.writeNodeStatusFile(IpReconfigConstants.NodeStatus.LOCAL_ROLLBACK.toString()); FileUtils.deleteFile(IpReconfigConstants.OLDIP_PATH); } } /** * If the local node is in LocalAware_ClusterPersistent status, the cluster might be using either * original IPs or the new IPs. * The local node should commit new IPs if quorum nodes of cluster (with new IPs) are available. * * @param newIpinfo * @param node_id * @param node_count * @throws Exception */ private static void handlePossibleJump(ClusterIpInfo newIpinfo, String node_id, int node_count) throws Exception { Integer node_id_number = Integer.valueOf(node_id.split("vipr")[1]); boolean bIpv4 = true; if (newIpinfo.getIpv4Setting().getNetworkNetmask().equals(PropertyConstants.IPV4_ADDR_DEFAULT)) { bIpv4 = false; } log.info("Checking if new IPs had already been commited..."); for (int i = 1; i < 24; i++) { // Here we assume each node would startup with functional network within at most 2m. // So we will check new IPs for 24 times (5s interval). log.info("Trying to ping other nodes ..."); int alivenodes = 0; for (String network_addr : newIpinfo.getIpv4Setting().getNetworkAddrs()) { if (ping(network_addr, bIpv4) == 0) { alivenodes++; } } // do not count local node alivenodes--; if (alivenodes > node_count / 2) { log.info("new IPs have been commited in quorum nodes of the cluster, so committing it in local node..."); applyIpinfo(newIpinfo, node_id, node_count, true); return; } Thread.sleep(5 * 1000); } log.info("New IPs seems have NOT been commited in quorum nodes of the cluster, so still using current IPs."); return; } private static int ping(String ip, boolean bIpv4) { String cmd = "ping"; String ipstr = ip; if (!bIpv4) { cmd += "6"; ipstr += "%eth0"; } final String[] cmds = { cmd, ipstr, "-c", "1" }; Exec.Result result = Exec.sudo(CMD_TIMEOUT, cmds); if (!result.exitedNormally() || result.getExitValue() != 0) { log.warn("Failed to ping {} with exit value: {}", ip, result.getExitValue()); } return result.getExitValue(); } }