/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.services.util;
import java.util.HashMap;
import java.util.Map;
import com.emc.storageos.model.property.PropertyConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A singleton class to probe the server hardware configurations such as memory, cpu and disk
* capacity etc.
*
*/
final public class ServerProbe {
private static final Logger log = LoggerFactory.getLogger(ServerProbe.class);
private static volatile ServerProbe instance = null;
// meet mini req for memory, cpu and at least one disk meet size mini req
private boolean meetMinimumRequirement = true;
private boolean allHasViPRPartition = false;
private String cpuCoreNum = null;
private String memorySize = null;
private String[] networkInterfaces = null;
private Map<String, String> diskCapacity = new HashMap<String, String>();;
private Map<String, Map<String, Object>> diskInfo = new HashMap<String, Map<String, Object>>();
private static final long CMD_TIMEOUT = 120 * 1000;
private ServerProbe() {
probeHardwares();
}
private void probeHardwares() {
probeNetworkInterface();
probeCpuAndMemory();
probeDisks();
if (!meetMinimumHwRequirement()) {
meetMinimumRequirement = false;
}
}
public static ServerProbe getInstance() {
if (instance == null) {
instance = new ServerProbe();
}
return instance;
}
public boolean isMetMinimumRequirement() {
return meetMinimumRequirement;
}
public String getCpuCoreNum() {
return cpuCoreNum;
}
public String getMemorySize() {
return memorySize;
}
public String[] getNetworkInterfaces() {
String[] result = null;
if(networkInterfaces != null){
result = networkInterfaces.clone();
}
return result;
}
public Map<String, String> getDiskCapacity() {
return diskCapacity;
}
public String getDiskCapacity(String name) {
return (String) diskInfo.get(name).get(PropertyConstants.NODE_PROBE_KEY_DISK_CAPACITY);
}
public boolean hasViPRPartition(String name) {
return (boolean) diskInfo.get(name).get(PropertyConstants.NODE_PROBE_KEY_DISK_HAS_VIPR_PARTITION);
}
public boolean allHasViPRPartition() {
return allHasViPRPartition;
}
public boolean diskMetMiniumSizeReq(String name) {
return (boolean) diskInfo.get(name).get(PropertyConstants.NODE_PROBE_KEY_DISK_MET_MIN_REQ);
}
private void probeNetworkInterface() {
networkInterfaces = getInterfaces();
}
private String[] getInterfaces() {
String[] cmds = { "/bin/sh", "-c",
"ls /sys/class/net | egrep -v lo | awk '{print $1}'" };
return PlatformUtils.executeCommand(cmds);
}
private void probeCpuAndMemory() {
cpuCoreNum = probeCpuCoreNumber();
memorySize = probeMemorySize();
}
/*
* Probe CPU core number on the server with cmd
*
* @return the number of CPU cores (e.g. String 2)
*/
private String probeCpuCoreNumber() {
final String[] cmds = { "cat", "/proc/cpuinfo" };
Exec.Result result = Exec.sudo(CMD_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Failed to get cpu core number with errcode: {}, error: {}",
result.getExitValue(), result.getStdError());
}
// processor info in /proc/cpuinfo is "processor : 0" etc.
int processor_no = 0;
String[] lines = result.getStdOutput().split("\n");
for (String line : lines) {
if (line.startsWith("processor")) {
if (processor_no < Integer.parseInt(line.split(":")[1].trim())) {
processor_no = Integer.parseInt(line.split(":")[1].trim());
}
}
}
return String.valueOf(++processor_no);
}
/*
* Probe memory size on the server with cmd (cat /proc/meminfo).
*
* @return the memory size in kB.
* For example String 6125560 is returned from "MemTotal: 6125560 kB"
*/
private String probeMemorySize() {
final String[] cmds = { "cat", "/proc/meminfo" };
Exec.Result result = Exec.sudo(CMD_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Failed to get cpu core number with errcode: {}, error: {}",
result.getExitValue(), result.getStdError());
}
String[] lines = result.getStdOutput().split("\n");
for (String line : lines) {
// Total memory line is like "MemTotal: 6125560 kB"
if (line.startsWith("MemTotal")) {
String delims = "[ ]+";
String[] tokens = line.split(delims);
return tokens[1];
}
}
return null;
}
/*
* Get Disk name and capacity
* By default, there are four disks in hypervisor platform.
* In ESX, {"sda 4G", "sdb 16G", "sdc 100G", "sdd 125K"}
* In Hyper-V, {"sda 4G", "sdb 16G", "sdc 100G", "sdd 1G"}
*
* @Return Example
* sda 8:0 0 50G 0
* sdb 8:16 0 60G 0 /project
* sdc 8:32 0 200G 0
* ...
*/
private String[] getDiskAndCapacityNumber() {
final String[] cmds = { "lsblk", "-i" };
Exec.Result result = Exec.sudo(CMD_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Failed to get cpu core number with errcode: {}, error: {}",
result.getExitValue(), result.getStdError());
}
return result.getStdOutput().split("\n");
}
private void probeDisks() {
log.info("Probing disks, should only run once.");
for (String disk : getDiskAndCapacityNumber()) {
if (!disk.startsWith("sd")) {
continue;
}
Map<String, Object> info = new HashMap<String, Object>();
String delims = "[ ]+";
String[] tokens = disk.split(delims);
String name = tokens[0].trim();
String diskName = name.contains("dev") ? name : "/dev/" + name;
String diskCap = tokens[3].trim();
diskCapacity.put(diskName, diskCap);
info.put(PropertyConstants.NODE_PROBE_KEY_DISK_CAPACITY, diskCap);
// check if disk meet mini req
log.debug("Run local disk {} minimum requirement check against {}",
diskName + "/" + diskCap, PropertyConstants.MIN_REQ_DISK_SIZE);
boolean metMinReq = false;
if (diskCap.endsWith(PropertyConstants.DISK_CAPACITY_UNIT_DEFAULT)) {
// Disk capacity is represented normally in Gigabyte for all disks.
// The disk4 is 1G in Hyper-V, while just about 125Kin ESXi Env.
if (Integer.parseInt(diskCap.split(PropertyConstants.DISK_CAPACITY_UNIT_DEFAULT)[0])
>= PropertyConstants.MIN_REQ_DISK_SIZE) {
metMinReq = true;
}
}
info.put(PropertyConstants.NODE_PROBE_KEY_DISK_MET_MIN_REQ, metMinReq);
// check if disk has vipr partition
boolean hasViPRPartition = false;
if (PlatformUtils.diskHasViprPartitions(diskName) != 0) {
log.warn("Disk {} has ViPR partition already", diskName);
hasViPRPartition = true;
}
info.put(PropertyConstants.NODE_PROBE_KEY_DISK_HAS_VIPR_PARTITION, hasViPRPartition);
diskInfo.put(diskName, info);
log.info("Probed diskInfo: {}", diskInfo);
}
allHasViPRPartition = allDisksHaveViPRPartition();
}
private boolean meetMinimumHwRequirement() {
log.info("Checking mininum requirement, should only run once.");
if (!runMemoryMinimumRequirementCheck()) {
return false;
}
if (!runCPUMinimumRequirementCheck()) {
return false;
}
if (!runDiskCapacityMinimumRequirementCheck()) {
return false;
}
return true;
}
// return true if all disks have ViPR partition
private boolean allDisksHaveViPRPartition() {
boolean noPartition = true;
for (String disk : diskInfo.keySet()) {
if (!(boolean) diskInfo.get(disk).get(PropertyConstants.NODE_PROBE_KEY_DISK_HAS_VIPR_PARTITION)) {
noPartition = false;
break;
}
}
return noPartition;
}
// return true if at least one disk met min req
private boolean runDiskCapacityMinimumRequirementCheck() {
log.debug("Run local disk {} minimum requirement check against {}",
diskInfo.keySet(), PropertyConstants.MIN_REQ_DISK_SIZE);
for (String disk : diskInfo.keySet()) {
if ((boolean) diskInfo.get(disk).get(PropertyConstants.NODE_PROBE_KEY_DISK_MET_MIN_REQ)) {
return true;
}
}
log.warn("No disk(s) {} meet minimum requirement for installation {}",
diskInfo, PropertyConstants.MIN_REQ_DISK_SIZE);
return false;
}
private boolean runCPUMinimumRequirementCheck() {
boolean ret = true;
log.debug("Run local CPU core {} minimum requirement check against {}",
cpuCoreNum, PropertyConstants.MIN_REQ_CPU_CORE);
try {
int cpuCore = Integer.parseInt(cpuCoreNum);
if (cpuCore < PropertyConstants.MIN_REQ_CPU_CORE) {
log.warn("CPU core number {} does not meet minimum requirement for installation {}",
cpuCore, PropertyConstants.MIN_REQ_CPU_CORE);
ret = false;
}
} catch (Exception e) {
log.error("Check min CPU requirement caught exception {} for {}", e.getMessage(), cpuCoreNum);
}
return ret;
}
private boolean runMemoryMinimumRequirementCheck() {
boolean ret = true;
log.debug("Run local memory size {} minimum requirement check against {}",
memorySize, PropertyConstants.MIN_REQ_MEM_SIZE);
try {
int memSize = Integer.parseInt(memorySize);
if (memSize < PropertyConstants.MIN_REQ_MEM_SIZE) {
log.warn("Memory size {} kB does not meet minimum requirement for installation {}",
memSize, PropertyConstants.MIN_REQ_MEM_SIZE + " kB");
ret = false;
}
} catch (Exception e) {
log.error("Check min memory requirement caught exception {} for {}", e.getMessage(), memorySize);
}
return ret;
}
}