/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.installer.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.emc.storageos.model.property.PropertyConstants; import com.emc.storageos.services.util.Configuration; import com.emc.storageos.services.util.MulticastUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.net.InetAddresses; public class InstallerUtil { private static final Logger log = LoggerFactory.getLogger(InstallerUtil.class); public static byte[] serialize(Object o) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); try { out.writeObject(o); } finally { out.close(); } return bos.toByteArray(); } public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException { Object obj = null; ByteArrayInputStream bis = new ByteArrayInputStream(data); ObjectInputStream in = new ObjectInputStream(bis); try { obj = in.readObject(); } finally { in.close(); } return obj; } /** * List cluster configurations scanned over network. * * @return configurations in a set */ /** * List cluster configurations scanned over network. * * @param netIf the network interface used to scan * @param serviceName the service name (e.g. release version) matched with the return set * @param scenario the boot mode matched with the return set * @return the set of cluster matched with the filters */ public static Set<Configuration> scanClusters(String netIf, String serviceName, String scenario) { log.info("Scanning over network {} for available configuration", netIf); Set<Configuration> availableClusters = new HashSet<Configuration>(); try { MulticastUtil multicastUtil = MulticastUtil.create(netIf); Map<String, Map<String, String>> clusters = multicastUtil.list(serviceName); for (Map<String, String> cluster : clusters.values()) { // get only the scenario asked for if (cluster != null && !cluster.isEmpty() && cluster.get(PropertyConstants.CONFIG_KEY_SCENARIO).equals(scenario)) { Configuration config = new Configuration(); config.setConfigMap(cluster); availableClusters.add(config); log.info("Scan found cluster: {}/{}", config.getNetworkVip(), config.toString()); } } multicastUtil.close(); } catch (IOException e) { log.error( "Scan available cluster Configuration failed with exception: %s", e.getMessage()); } log.info("Scan found {} cluster(s) for '{}'", availableClusters.size(), scenario); return availableClusters; } /** * Check if the network addresses are on the same sub network. * * @param map1 the ipv4 addresses * @param map2 the ipv6 addresses * @return the list of names not on the sub network */ public static List<String> checkAddressesOnSameSubNetwork(LinkedHashMap<String, String> map1, LinkedHashMap<String, String> map2) { List<String> errorMsg = new ArrayList<String>(); // check for ipv4 if (!map1.isEmpty()) { Map<String, String> ipv4 = new HashMap<String, String>(); ipv4.putAll(map1); String mask = ipv4.get(InstallerConstants.DISPLAY_LABEL_IPV4_NETMASK); // exclude netmask from the list when check on same network, add it back after ipv4.remove(InstallerConstants.DISPLAY_LABEL_IPV4_NETMASK); if (!isOnSameNetworkIPv4(ipv4.values(), mask)) { errorMsg.add("IPv4 addresses are not in the same subnet"); log.warn("IPv4 addresses are not in the same subnet"); } } // check for ipv6 if (!map2.isEmpty()) { Map<String, String> ipv6 = new HashMap<String, String>(); ipv6.putAll(map2); String prefixLength = ipv6.get(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX); // exclude prefix length from the list when check on same network ipv6.remove(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX); if (!isOnSameNetworkIPv6(ipv6.values(), prefixLength)) { errorMsg.add("IPv6 addresses are not in the same subnet"); log.warn("IPv6 addresses are not in the same subnet"); } } return errorMsg; } /** * Validate Ipv4 and Ipv6 address format * * @param map1 the ipv4 addresses * @param map2 the ipv6 addresses * @return the invalid list for both ipv4 and ipv6 */ public static List<String> checkInvalidAddress(LinkedHashMap<String, String> map1, LinkedHashMap<String, String> map2) { List<String> invalidIpv4 = validateIPv4Addresses(map1); List<String> invalidIpv6 = validateIPv6Address(map2); List<String> invalid = new ArrayList<String>(); invalid.addAll(invalidIpv4); invalid.addAll(invalidIpv6); if (!invalid.isEmpty()) { log.warn("There are invalid addresses entered: {}/{}", invalid.size(), invalid); } return invalid; } /** * Check if network is configured (not default). * * @param ipv4 map of IPv4 addresses * @param ipv6 map of IPv6 addresses * @return true if both IPv4 and IPv6 addresses are not configured, false otherwise. */ public static boolean ipAddressNotConfigured(LinkedHashMap<String, String> ipv4, LinkedHashMap<String, String> ipv6) { boolean ipv4IsDefault = isIpv4Default(ipv4); boolean ipv6IsDefault = isIpv6Default(ipv6); if (ipv4IsDefault && ipv6IsDefault) { return true; } else { return false; } } private static boolean isIpv6Default( LinkedHashMap<String, String> ipv6) { for (Entry<String, String> entry : ipv6.entrySet()) { if (entry.getValue().equals(PropertyConstants.IPV6_ADDR_DEFAULT)) { return true; } } return false; } private static boolean isIpv4Default( LinkedHashMap<String, String> ipv4) { for (Entry<String, String> entry : ipv4.entrySet()) { if (entry.getValue().equals(PropertyConstants.IPV4_ADDR_DEFAULT)) { return true; } } return false; } /** * Check if there are duplicate addresses entered * * @param map1 the ipv4 addresses * @param map2 the ipv6 addresses * @return the duplicate list for both ipv4 and ipv6 */ public static List<String> checkDuplicateAddress(LinkedHashMap<String, String> map1, LinkedHashMap<String, String> map2) { List<String> dup1 = checkDuplicate(map1); List<String> dup2 = checkDuplicate(map2); List<String> dup = new ArrayList<String>(); dup.addAll(dup1); dup.addAll(dup2); if (!dup.isEmpty()) { log.warn("There are duplicate addresses entered: {}/{}", dup.size(), dup); } return dup; } /* * Remove the ": " from the display label in the map key * * @param map the map to process * * @return the new map */ private static Map<String, String> mapHelper(LinkedHashMap<String, String> map) { Map<String, String> ret = new HashMap<String, String>(); for (String key : map.keySet()) { ret.put(key.replace(": ", ""), map.get(key)); } return ret; } /* * Help method to check if the ipv4 addresses are on the same sub net * * @param ipv4 addrList the list of addresses to be checked * * @param mask the netmask * * @return true if on the same net false otherwise */ private static boolean isOnSameNetworkIPv4(Collection<String> addrList, String mask) { try { byte[] m = InetAddress.getByName(mask).getAddress(); for (int i = 0; i < m.length; i++) { List<Integer> values = new ArrayList<Integer>(); for (String ip : addrList) { byte[] a = InetAddress.getByName(ip).getAddress(); values.add(a[i] & m[i]); } // check if all values are same (on the same subnet) if (!areAllEqual(values)) { return false; } } } catch (UnknownHostException e) { log.warn("Got an UnknownHostException: {}", e.getMessage()); return false; } return true; } /* * Help method to check if the ipv6 addresses are on the same sub net * * @param ipv6 addrList the list of addresses to be checked * * @param the prefix length of the subnet * * @return true if on the same net false otherwise */ private static boolean isOnSameNetworkIPv6(Collection<String> addrList, String prefixLength) { try { byte[] ipv6Netmask = new byte[16]; int prefixL = Integer.valueOf(prefixLength); int numberOfFullByte = prefixL / 8; for (int i = 0; i < numberOfFullByte; i++) { ipv6Netmask[i] = (byte) 255; // Those bytes are all 1s } int shiftAmount = 8 - prefixL % 8; ipv6Netmask[numberOfFullByte] = (byte) (255 & (~0 << shiftAmount)); for (int i = numberOfFullByte + 1; i < 16; i++) { ipv6Netmask[i] = (byte) 0; // Those bytes are all 0s } for (int i = 0; i < ipv6Netmask.length; i++) { List<Integer> values = new ArrayList<Integer>(); for (String ip : addrList) { byte[] a = InetAddress.getByName(ip).getAddress(); values.add(a[i] & ipv6Netmask[i]); } // check if all values are same (on the same subnet) if (!areAllEqual(values)) { return false; } } } catch (UnknownHostException e) { log.warn("Got an UnknownHostException: {}", e.getMessage()); return false; } return true; } /* * Help method to check all the values in the list are the same * * @param values the values to be checked * * @return true if all the same value, false otherwise */ private static boolean areAllEqual(List<Integer> values) { if (values.size() == 0) { return true; } int checkValue = values.get(0); for (int value : values) { if (value != checkValue) { return false; } } return true; } /* * Help method to check if duplicate addresses in the map * * @param map1 * * @return */ private static List<String> checkDuplicate(LinkedHashMap<String, String> inputMap) { Map<String, String> map = mapHelper(inputMap); List<String> dup = new ArrayList<String>(); Multimap<String, String> multiMap = Multimaps.invertFrom(Multimaps.forMap(map), HashMultimap.<String, String> create()); for (Entry<String, Collection<String>> entry : multiMap.asMap().entrySet()) { Collection<String> values = entry.getValue(); List<String> sortValues = new ArrayList<String>(values); Collections.sort(sortValues); if (values.size() > 1) { if (!entry.getKey().equals(PropertyConstants.IPV4_ADDR_DEFAULT) && !entry.getKey().equals(PropertyConstants.IPV6_ADDR_DEFAULT)) { // more than one entry has the same value, duplicated value found dup.add(StringUtils.join(sortValues.toArray(), ", ")); } } } log.debug("Found {} duplicates in map {}", dup.size(), inputMap); return dup; } /* * Validate ipv4 addresses for valid IPv4 format * * @param map ipv4 address map * * @return a list of invalid names */ private static List<String> validateIPv4Addresses(LinkedHashMap<String, String> map) { List<String> statusList = new ArrayList<String>(); for (Entry<String, String> entry : map.entrySet()) { boolean isValid = validateIpv4Addr(entry.getValue()); if (!isValid) { statusList.add(entry.getKey().replace(": ", "")); } } return statusList; } /* * Validate ipv6 addresses for valid IPv6 format * * @param map ipv6 address map * * @return a list of invalid names */ private static List<String> validateIPv6Address(LinkedHashMap<String, String> map) { List<String> statusList = new ArrayList<String>(); Map<String, String> mapToCheck = new HashMap<String, String>(); mapToCheck.putAll(map); // validate Ipv6 prefix length String prefixLength = mapToCheck.get(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX); if (prefixLength == null || prefixLength.isEmpty() || Integer.valueOf(prefixLength) < 0 || Integer.valueOf(prefixLength) > 128) { statusList.add(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX.replace(": ", "")); } // exclude Ipv6 prefix length for validation mapToCheck.remove(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX); for (Entry<String, String> entry : mapToCheck.entrySet()) { boolean isValid = validateIpv6Addr(entry.getValue()); if (!isValid) { statusList.add(entry.getKey().replace(": ", "")); } } return statusList; } /* * Validate value is an IpAddr. * * @param value * * @return */ private static boolean validateIpAddr(String value) { return InetAddresses.isInetAddress(value); } /* * Validate value is an IPv4 Address * * @param value * * @return */ private static boolean validateIpv4Addr(String value) { try { return validateIpAddr(value) && InetAddresses.forString(value) instanceof Inet4Address; } catch (Exception e) { return false; } } /* * Validate value is an IPv6 Address * * @param value * * @return */ private static boolean validateIpv6Addr(String value) { try { return validateIpAddr(value) && InetAddresses.forString(value) instanceof Inet6Address; } catch (Exception e) { return false; } } // pad with " " to the right to the given length (n) /** * Pad white space to the right of the string to the given length * * @param s the string * @param n the length of the padded string * @return the padded string */ public static String padRight(String s, int n) { return String.format("%1$-" + n + "s", s); } /* * IPv4 Network addresses conversion table between display label and property key */ private static final Map<String, String> ipv4NameConversionTable; static { Map<String, String> aMap = new HashMap<String, String>(); for (int i = 1; i <= 5; i++) { aMap.put(String.format(InstallerConstants.DISPLAY_LABEL_IPV4_NODE_ADDR, i), String.format(PropertyConstants.IPV4_ADDR_KEY, i)); } aMap.put(InstallerConstants.DISPLAY_LABEL_IPV4_VIP, PropertyConstants.IPV4_VIP_KEY); aMap.put(InstallerConstants.DISPLAY_LABEL_IPV4_NETMASK, PropertyConstants.IPV4_NETMASK_KEY); aMap.put(InstallerConstants.DISPLAY_LABEL_IPV4_GATEWAY, PropertyConstants.IPV4_GATEWAY_KEY); ipv4NameConversionTable = Collections.unmodifiableMap(aMap); } /* * Convert the IPv4 network display name/label to property key * * @param inMap the input map * * @return the map with property key */ public static LinkedHashMap<String, String> convertIpv4DisplayNameToPropertyKey(LinkedHashMap<String, String> inMap) { LinkedHashMap<String, String> outMap = new LinkedHashMap<String, String>(); for (Entry<String, String> entry : inMap.entrySet()) { String key = entry.getKey(); if (ipv4NameConversionTable.containsKey(key)) { outMap.put(ipv4NameConversionTable.get(key), entry.getValue()); } else { outMap.put(key, entry.getValue()); } } return outMap; } /** * Get IPv4 network addresses with display labels * * @return the map with display labels */ public static LinkedHashMap<String, String> getIpv4DisplayMap(LinkedHashMap<String, String> networkIpv4Config) { Map<String, String> conversionTable = ovfKeyToDisplayTag(ipv4NameConversionTable); LinkedHashMap<String, String> outMap = new LinkedHashMap<String, String>(); for (Entry<String, String> entry : networkIpv4Config.entrySet()) { String key = entry.getKey(); if (conversionTable.containsKey(key)) { outMap.put(conversionTable.get(key), entry.getValue()); } else { outMap.put(key, entry.getValue()); } } return outMap; } /* * IPv6 Network addresses conversion table between display label and property key */ private static final Map<String, String> ipv6NameConversionTable; static { Map<String, String> aMap = new HashMap<String, String>(); for (int i = 1; i <= 5; i++) { aMap.put(String.format(InstallerConstants.DISPLAY_LABEL_IPV6_NODE_ADDR, i), String.format(PropertyConstants.IPV6_ADDR_KEY, i)); } aMap.put(InstallerConstants.DISPLAY_LABEL_IPV6_VIP, PropertyConstants.IPV6_VIP_KEY); aMap.put(InstallerConstants.DISPLAY_LABEL_IPV6_PREFIX, PropertyConstants.IPV6_PREFIX_KEY); aMap.put(InstallerConstants.DISPLAY_LABEL_IPV6_GATEWAY, PropertyConstants.IPV6_GATEWAY_KEY); ipv6NameConversionTable = Collections.unmodifiableMap(aMap); } /* * Convert the IPv6 network display name/label to property key * * @param inMap the input map * * @return the map with property key */ public static LinkedHashMap<String, String> convertIpv6DisplayNameToPropertyKey(LinkedHashMap<String, String> inMap) { LinkedHashMap<String, String> outMap = new LinkedHashMap<String, String>(); for (Entry<String, String> entry : inMap.entrySet()) { String key = entry.getKey(); if (ipv6NameConversionTable.containsKey(key)) { outMap.put(ipv6NameConversionTable.get(key), entry.getValue()); } else { outMap.put(key, entry.getValue()); } } return outMap; } /** * Get IPv6 network addresses with display labels * * @return the map with display labels */ public static LinkedHashMap<String, String> getIpv6DisplayMap(LinkedHashMap<String, String> networkIpv6Config) { Map<String, String> conversionTable = ovfKeyToDisplayTag(ipv6NameConversionTable); LinkedHashMap<String, String> outMap = new LinkedHashMap<String, String>(); for (Entry<String, String> entry : networkIpv6Config.entrySet()) { String key = entry.getKey(); if (conversionTable.containsKey(key)) { outMap.put(conversionTable.get(key), entry.getValue()); } else { outMap.put(key, entry.getValue()); } } return outMap; } // help method to reverse map key value pairs private static Map<String, String> ovfKeyToDisplayTag(Map<String, String> map) { Map<String, String> rev = new HashMap<String, String>(); for (Entry<String, String> entry : map.entrySet()) { rev.put(entry.getValue(), entry.getKey()); } return rev; } }