/*
* Copyright (c) 2008-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.installer.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import com.emc.storageos.model.property.PropertyConstants;
import com.emc.storageos.services.util.ServerProbe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emc.storageos.services.util.Configuration;
import com.emc.storageos.services.util.Exec;
public class InstallerOperation {
private static final Logger log = LoggerFactory.getLogger(InstallerOperation.class);
private static final String LIST_DISK_CMD = "lsblk -i|grep ^sd|awk '{print $1 \" \" $4}'";
private static final String GENISO_CMD = "/opt/storageos/bin/geniso";
private static final long CMD_DEFAULT_TIMEOUT = 60 * 1000; // 1 min
private static final long _INSTALLER_PARTITION_TIMEOUT = 600 * 1000; // 10 min
private static final long _INSTALLER_COPYROOTIMG_TIMEOUT = 3600 * 1000; // 60 min
private static final long GENERATE_ISO_FILE_TIMEOUT = 30000; // 30 seconds timeout
private static final long PROBE_OVFENV_PARTITION_TIMEOUT = 60 * 1000; // 1 min
public static int initializeDisk(String disk) {
final String[] cmds = { "/etc/mkdisk.sh", "native", disk };
Exec.Result result = Exec.sudo(_INSTALLER_PARTITION_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Init disk failed with exit value is: {}",
result.getExitValue());
}
return result.getExitValue();
}
public static int installImage(String disk, String pathToImage) {
final String[] cmds = { "/etc/systool", "--bootfs-dev=" + disk + "1",
"--bootfs-mntp=/mnt/.volumes/bootfs", "--DO_NOT_INCLUDE=yes",
"--install-baremetal", "/.volumes/bootfs/vipr-*/rootimg" };
Exec.Result result = Exec.sudo(_INSTALLER_COPYROOTIMG_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Install Image failed with exit value is: {}",
result.getExitValue());
}
return result.getExitValue();
}
private static String formatProperties(Configuration config) {
// TODO: we could convert this to properties and make it simpler, as the new getovfproperties accepts both.
// Compose cluster configuration string
StringBuffer clusterprops = new StringBuffer();
clusterprops.append(" <PropertySection>\n");
// Looping through each property and add them to the StringBuffer
for (Map.Entry<String, String> entry : config.getOVFProps().entrySet()) {
clusterprops.append(" <Property oe:key=\"");
clusterprops.append(entry.getKey());
clusterprops.append("\" oe:value=\"");
clusterprops.append(entry.getValue());
clusterprops.append("\"/>\n");
}
clusterprops.append(" </PropertySection>\n");
// Generate the final string for ovf-env.xml file
StringBuffer props = new StringBuffer();
props.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
props.append("<Environment\n");
props.append(" oe:id=\"").append(config.getNodeId()).append("\">\n"); // Append the nodeId first
props.append(" <PlatformSection>\n");
props.append(" <Kind>Commodity hardware</Kind>\n");
props.append(" <Version>No hypervisor</Version>\n");
props.append(" </PlatformSection>\n");
props.append(clusterprops.toString());
if (1 == config.getNodeCount()) {
props.append("<Entity />\n");
} else {
for (int i = 1; i <= config.getNodeCount(); i++) {
StringBuffer tmpNodeId = new StringBuffer();
tmpNodeId.append("vipr");
tmpNodeId.append(i);
if (!config.getNodeId().equals(tmpNodeId.toString())) {
props.append("<Entity oe:id=\"").append(tmpNodeId).append("\">\n");
props.append(clusterprops.toString());
props.append("</Entity>\n");
}
}
}
// TODO: for native installed env, put selected data disk and netif info into into xml.
props.append("</Environment>\n");
return props.toString();
}
public static void installISOImage(Configuration config) {
String ovfProperties = formatProperties(config);
byte[] bFile = ovfProperties.getBytes();
String xmlFilePath = "/tmp/ovf-env.xml";
File xmlFile = new File(xmlFilePath);
try {
writeFile(xmlFilePath, bFile);
} catch (IOException e1) {
log.error("Write to xml file failed with exception: {}",
e1.getMessage());
xmlFile.delete();
}
File isoFile = new File("/tmp/ovf-env.iso");
String[] genISOImageCommand = { GENISO_CMD, "--label", "CDROM", "-f", "/tmp/ovf-env.xml", "-o", "/tmp/ovf-env.iso", "ovf-env.xml",
"4096" };
Exec.Result result = Exec.sudo(GENERATE_ISO_FILE_TIMEOUT, genISOImageCommand);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Generating ISO image failed with exit value: {}, err:{}",
result.getExitValue(), result.getStdError());
}
try {
String cdPartition = "";
if (config.isInstallMode()) {
// For "install" mode:
// 1. Only for Baremetal installation, all partitions are in same disk for now
// 2. In Hyper-V and KVM, installation is under the way same as ovftool etc.
cdPartition = config.getHwConfig().get(PropertyConstants.PROPERTY_KEY_DISK) + "4";
} else {
// For "config" and "redeploy" mode:
// 1. Need to probe ovfenv partition for baremetal, hyper-v and kvm env.
cdPartition = InstallerOperation.probeOvfenvPartition();
log.info("Probed ovfenv partition {}", cdPartition);
}
Path path = Paths.get("/tmp/ovf-env.iso");
byte[] data = Files.readAllBytes(path);
FileOutputStream fileOuputStream = new FileOutputStream(cdPartition);
fileOuputStream.write(data);
fileOuputStream.close();
} catch (IOException e) {
log.error("Creating ISO image partition failed with exception: {}",
e.getMessage());
isoFile.delete();
}
xmlFile.delete();
isoFile.delete(); // Those files are temporary, we delete them after using
}
private static String getVendorInfo(String disk) {
String cmd = "cat /sys/block/" + disk + "/device/vendor";
return executeCommand(cmd).trim();
}
private static String getRevInfo(String disk) {
String cmd = "cat /sys/block/" + disk + "/device/rev";
return executeCommand(cmd).trim();
}
private static String getModelInfo(String disk) {
String cmd = "cat /sys/block/" + disk + "/device/model";
return executeCommand(cmd).trim();
}
public static String probeOvfenvPartition() {
String[] cmds = { "/bin/sh", "-c", "/etc/getovfproperties --probe-ovfenv-partition" };
String[] disks = executeCommand(cmds);
if (disks.length == 0) {
log.warn("Probing did not find local ovfenv partition.");
return null;
} else {
return disks[0];
}
}
public static int probeOvfenvEmptyPartition() {
final String[] cmds = { "/etc/getovfproperties", "--probe-ovfenv-empty-partition" };
Exec.Result result = Exec.sudo(PROBE_OVFENV_PARTITION_TIMEOUT, cmds);
if (!result.exitedNormally() || result.getExitValue() != 0) {
log.error("Failed to probe empty ovfenv partition with exit value is: {}",
result.getExitValue());
}
return result.getExitValue();
}
public static String getBootDeviceType() {
String[] cmds = { "/bin/sh", "-c", "/usr/bin/grep bootfs_dev=/dev/sr /proc/cmdline" };
String[] results = executeCommand(cmds);
if (results.length == 0) {
log.info("system boot from hard disk.");
return "harddisk";
} else {
log.info("system boot from cdrom.");
return "cdrom";
}
}
/*
* Probe the disk name and its capacity.
*
* @return disk name and capacity String (e.g. "sda 200G")
*/
private static String[] getDiskAndCapacity() {
String[] cmds = { "/bin/sh", "-c", LIST_DISK_CMD };
return executeCommand(cmds);
}
/**
* Get the disk name out from the input String.
*
* @param input the String to be parsed (e.g. "sda 200G")
* @return disk name (e.g. "sda")
*/
public static String parseDiskString(String input) {
String delims = "[ ]+";
String[] tokens = input.split(delims);
return tokens[0];
}
public static String[] getDiskInfo() {
String[] diskAndCap = getDiskAndCapacity();
ArrayList<String> info = new ArrayList<String>();
for (String s : diskAndCap) {
String disk = parseDiskString(s);
String i = getVendorInfo(disk) + " "
+ getModelInfo(disk) + " " + getRevInfo(disk);
info.add("/dev/" + s + " " + i);
}
return info.toArray(new String[info.size()]);
}
private static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
private static String[] executeCommand(String[] commands) {
StringBuffer output = new StringBuffer();
ArrayList<String> out = new ArrayList<String>();
Process p;
try {
p = Runtime.getRuntime().exec(commands);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\t");
out.add(line);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return out.toArray(new String[out.size()]);
}
private static void writeFile(String filePath, byte[] content) throws IOException {
FileOutputStream fileOuputStream = new FileOutputStream(filePath);
fileOuputStream.write(content);
fileOuputStream.close();
}
/**
* Check local node hardware (i.e. Memory size, CPU core, Disk Capacity)
* are the same as selected one.
*
* @param name the selected disk name
* @param size the selected disk size
* @return true if they are the same, false otherwise
*/
private static boolean hasSameDiskInfo(String name, String size) {
Map<String, String> localDiskCap = ServerProbe.getInstance().getDiskCapacity();
if (name != null && size != null && size.equals(localDiskCap.get(name))) {
return true;
}
log.warn("Local disk(s) {} are not the same as selected cluster {}",
localDiskCap, name + "=" + size);
return false;
}
/**
* Check local node hardware (i.e. Memory size, CPU core, Disk Capacity)
* are the same as in the input map.
*
* @param hwMap the input map with hardware info.
* @param checkDisk a flag to indicate if disk info needs to be compared
* @return true if they are the same; false otherwise
*/
public static String compareHardware(Map<String, String> hwMap, boolean checkDisk) {
// this is the String in kB
String localMemSizeStr = ServerProbe.getInstance().getMemorySize();
String localCpuCoreStr = ServerProbe.getInstance().getCpuCoreNum();
hwMap.get(PropertyConstants.PROPERTY_KEY_DISK_CAPACITY);
if (!localMemSizeStr.equals(hwMap.get(PropertyConstants.PROPERTY_KEY_MEMORY_SIZE))) {
log.warn("Local memory {} is not the same as selected cluster {}",
localMemSizeStr, hwMap.get(PropertyConstants.PROPERTY_KEY_MEMORY_SIZE));
return String.format("Local memory {%s} is not the same as selected cluster {%s}",
localMemSizeStr, hwMap.get(PropertyConstants.PROPERTY_KEY_MEMORY_SIZE));
}
if (!localCpuCoreStr.equals(hwMap.get(PropertyConstants.PROPERTY_KEY_CPU_CORE))) {
log.warn("Local CPU core number {} is not the same as selected cluster {}",
localCpuCoreStr, hwMap.get(PropertyConstants.PROPERTY_KEY_CPU_CORE));
return String.format("Local CPU core number {%s} is not the same as selected cluster {%s}",
localCpuCoreStr, hwMap.get(PropertyConstants.PROPERTY_KEY_CPU_CORE));
}
if (checkDisk && !hasSameDiskInfo(hwMap.get(PropertyConstants.PROPERTY_KEY_DISK),
hwMap.get(PropertyConstants.PROPERTY_KEY_DISK_CAPACITY))) {
log.warn("Local disk(s) are not the same as selected cluster capacity {}",
hwMap.get(PropertyConstants.PROPERTY_KEY_DISK_CAPACITY));
return String.format("Local disk(s) are not the same as selected cluster capacity {%s}",
hwMap.get(PropertyConstants.PROPERTY_KEY_DISK_CAPACITY));
}
return null;
}
}