content: /* The contents of this file are subject to the terms
   * of the Common Development and Distribution License
   * (the License). You may not use this file except in
   * compliance with the License.
   *
   * You can obtain a copy of the License at
   * http://www.sun.com/cddl/cddl.html or
   * install_dir/legal/LICENSE
   * See the License for the specific language governing
   * permission and limitations under the License.
   *
   * When distributing Covered Code, include this CDDL
   * Header Notice in each file and include the License file
   * at install_dir/legal/LICENSE.
   * If applicable, add the following below the CDDL Header,
   * with the fields enclosed by brackets [] replaced by
   * your own identifying information:
   * "Portions Copyrighted [year] [name of copyright owner]"
   *
   * $Id$
   *
   * Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
   */
  package com.sun.faban.harness.engine;
  
  import com.sun.faban.common.Command;
  import com.sun.faban.common.CommandHandle;
  import com.sun.faban.harness.ConfigurationException;
  import com.sun.faban.harness.ParamRepository;
  import com.sun.faban.harness.common.Config;
  import com.sun.faban.harness.common.Run;
  
  import java.io.File;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.PrintStream;
  import java.rmi.RemoteException;
  import java.text.DateFormat;
  import java.util.*;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  /**
   * Class: ServerConfig.java
   * This class manages the configuration of the server machines.
   * It provides methods to capture system configuration, change
   * the number of processors, get system log messages during a
   * benchmark run, etc.
   * WARNING: It is specific to Solaris and non-portable.
   *
   * @see GenericBenchmark
   * @author Ramesh Ramachandran
   */
  class ServerConfig {
      Run run;
      ParamRepository par;
      static String linesep = "<hr>";
      String master;
      CmdService cmds;
      Logger logger;
      List<ParamRepository.HostConfig> hostConfigs;
  
      public ServerConfig(Run r, ParamRepository par)
              throws ConfigurationException {
          this.par = par;
          run = r;
          cmds = CmdService.getHandle();
          logger = Logger.getLogger(this.getClass().getName());
          master = CmdService.getHandle().getMaster();
          hostConfigs = par.getHostConfigs();
      }
  
      /**
       * Get system configuration
       * This method retrieves the various system parameters from the
       * ParamRepository.
       * It gather /etc/system, prtdiag, psrinfo, uname, ps , vxprint
       * info from the server machines and logs them to the system log.
       * @return Whether the system configuration was successfully obtained.
       */
      public boolean get() {
          // Generate name of system log file - system.log
          String syslogfile = run.getOutDir() + "sysinfo.";
          boolean success = true;
  
          for(ParamRepository.HostConfig hostConfig : hostConfigs) {
              for (String host : hostConfig.hosts) {
                  String machineName = cmds.getHostName(host);
                  try {
                      File f = new File(syslogfile + machineName + ".html");
  
                      // In case we have multiple interfaces, the file may
                      // already exist. We don't want to spend the time doing
                      // the same thing over and over again.
                      if (f.exists())
                          continue;
  
                      // Get system info
                      PrintStream syslog =
                                      new PrintStream(new FileOutputStream(f));
                      Command sysinfo = new Command("sysinfo");
                      sysinfo.setStreamHandling(Command.STDOUT, Command.CAPTURE);
                      CommandHandle handle = cmds.execute(host, sysinfo, null);
                      byte[] info = handle.fetchOutput(Command.STDOUT);
  
                      // Write header and info to file.
                      syslog.println("<html><head><title>System Info for Server "
                              + machineName + "</title></head><body>");
  
                      syslog.write(info);
  
                      // Get User Commands output if specified
                      if (hostConfig.userCommands != null &&
                              hostConfig.userCommands.trim().length() > 0) {
                          String[] cmdStrings = hostConfig.userCommands.
                                                              split(";");
                          for (String cmdString : cmdStrings) {
                              Command c = new Command(cmdString);
                              c.setStreamHandling(Command.STDOUT,Command.CAPTURE);
                              logger.info("Executing '" + cmdString + "'");
                              handle = cmds.execute(host, c, null);
                              info = handle.fetchOutput(Command.STDOUT);
                              if (info != null) {
                                  syslog.println(linesep);
                                  syslog.println("<h3>" + hostConfig.userCommands+
                                          " on server " + machineName + "</h3>");
                                  syslog.println("<pre>\n");
                                  syslog.write(info);
                                  syslog.println("\n</pre>");
                              }
                          }
                      }
                      syslog.println("</body></html>");
                      syslog.close();
                  } catch (Exception e) {
                      logger.log(Level.SEVERE, "Failed to collect system info " +
                                              "for host " + machineName + '.', e);
                      success = false;
                  }
              }
          }
          return success;
      }
  
      /**
       * Set system configuration.
       * Currently the only thing we set is the number of cpus.
       * @param cmds object to use
       * @return true/false depending on whether we were successful or not
       */
      public boolean set(CmdService cmds) {
  
          for (ParamRepository.HostConfig hostConfig : hostConfigs) {
              String[] hosts = hostConfig.hosts;
              int[] cpus = hostConfig.numCpus;
              int numCPUs = 0;
  
              if (cpus == null)
                  continue;
  
              if (cpus.length == 1 && cpus[0] == 0)
                  continue;
  
              // cpus can be of size 0 - don't set it, 1 - set the same for all,
              // or the number of hosts.
              if (cpus.length > 1 && cpus.length != hosts.length) {
                  logger.severe("Entries in the \"cpus\" field must be 1 or " +
                          "the number of hosts, or blank. CPU entry mismatch. " +
                          "Please check configuration.");
                  return false;
              }
  
              for(int j = 0; j < hosts.length; j++) {
                  if(cpus.length == 1)
                      numCPUs = cpus[0];
                  else
                      numCPUs = cpus[j];
  
                  // User don't want to reconfigure this system.
                  if(numCPUs == 0)
                      continue;
  
                  try {
                      // We first turn on all cpus, then turn off enough of them
                      // to get the required number
                      Command cmd = new Command(Config.BIN_DIR + "fastsu",
                              "/usr/sbin/psradm", "-a", "-n");
                      logger.config("Turning on all cpus on " + hosts[j]);
                      cmds.execute(hosts[j], cmd, null);
  
                      cmd = new Command("/usr/sbin/psrinfo");
                      cmd.setStreamHandling(Command.STDOUT, Command.CAPTURE);
                      logger.fine("Getting cpus");
                      CommandHandle handle = cmds.execute(hosts[j], cmd, null);
                      byte[] buffer = handle.fetchOutput(Command.STDOUT);
  
                      if (buffer != null) {
                          StringTokenizer t =
                                  new StringTokenizer(new String(buffer), "\n");
  
                          ArrayList<Integer> cpuList = new ArrayList<Integer>();
                          boolean isMultiCore = false;
  
                          while (t.hasMoreTokens()) {
                              String line = t.nextToken();
                              // build list of cpus
                              Integer cpuId = Integer.valueOf((
                                      new StringTokenizer(line)).nextToken().
                                      trim());
                              if((isMultiCore == false) &&
                                      (cpuId.intValue() > 511))
                                  isMultiCore = true;
                              cpuList.add(cpuId);
                          }
                          logger.info("Total number of CPUs is " +
                                  cpuList.size()/(isMultiCore ? 2 : 1));
  
                          // The index gets changed when you remove elements
                          // Remove number of CPUs configured to be used for this
                          // server
                          for(int k = 0; k < numCPUs; k++) {
                              if(isMultiCore) {
                                  Integer cpuId = new Integer(
                                          (cpuList.get(0)).intValue() + 512);
                                  cpuList.remove(cpuId);
                              }
                              cpuList.remove(0);
                          }
  
                          logger.info("Number of CPUs turned off is " +
                                  cpuList.size()/(isMultiCore ? 2 : 1));
  
                          // The remaining CPUs in the list have to be turned off.
  
                          if (cpuList.size() > 0) {
                              ArrayList<String> offlineCmd = new ArrayList<String>();
                              offlineCmd.add(Config.BIN_DIR + "fastsu");
                              offlineCmd.add("/usr/sbin/psradm");
                              offlineCmd.add("-f");
  
                              for(int k = 0; k < cpuList.size(); k++)
                                  offlineCmd.add(cpuList.get(k).toString());
  
                              logger.info("Off-lining CPUs with command: " +
                                      offlineCmd);
                              cmd = new Command(offlineCmd);
                              cmds.execute(hosts[j], cmd, null);
                          }
                      } else {
                          logger.severe("Could not set CPUs on server " +
                                  hosts[j]);
                      }
                  } catch (Exception ie) {
                      logger.log(Level.SEVERE, "Failed to set Server Config.", ie);
                      return(false);
                  }
              }
          }
          return(true);
      }
  
      /**
       * Get system logs for benchmark duration.
       * This method captures the relevant portion of var/adm/messages
       * to the system report file for the benchmark run.
  
       * @param startTime of benchmark run
       * @param endTime of benchmark run
       */
      public void report(long startTime, long endTime) {
  
          String sysfile = run.getOutDir() + "system.report";
          PrintStream syslog = null;
          DateFormat df = DateFormat.getDateTimeInstance(
                              DateFormat.MEDIUM, DateFormat.LONG, Locale.US);
          String start = df.format(new Date(startTime));
          String end = df.format(new Date(endTime));
  
          String startMon = start.substring(0, 3);
          int ind = start.indexOf(',');
          String startDay = start.substring(4, ind);
          ind = ind + 7; // skip over ' 1999 ' to get to time
          String stime = start.substring(ind, start.indexOf(' ', ind));
          logger.fine("Run started Month = " + startMon +
                  " Day = " + startDay + " Time = " + stime);
  
          String endMon = end.substring(0, 3);
          ind = end.indexOf(',');
          String endDay = end.substring(4, ind);
          ind = ind + 7; // skip over ' 1999 ' to get to time
          String etime = end.substring(ind, end.indexOf(' ', ind));
          logger.fine("Run ended Month = " + endMon
                  + " Day = " + endDay + " Time = " + etime);
  
          // Now, get /var/adm/messages and look for messages between
          // start and end
          CommandHandle handle;
          Command c = new Command("messages", "\"" +
                  startMon + " " + startDay + " " + stime + "\"",
                  "\""  + endMon + " " + endDay + " " + etime + "\"");
          c.setStreamHandling(Command.STDOUT, Command.CAPTURE);
          logger.fine("Getting system messages");
  
          for (ParamRepository.HostConfig hostConfig : hostConfigs) {
              for(String host : hostConfig.hosts) {
                  String machineName = cmds.getHostName(host);
                  File f = new File(sysfile + "." + machineName);
                  f.delete();
                  try {
                      syslog = new PrintStream(new FileOutputStream(f));
                      handle = cmds.execute(host, c, null);
                      byte[] messages = handle.fetchOutput(Command.STDOUT);
                      syslog.println(linesep);
                      syslog.println("System messages during run from server " +
                              host);
                      syslog.println("\n");
                      if (messages != null) // Null if no messages.
                          syslog.write(messages);
                      syslog.println("\n");
                  } catch (RemoteException e) {
                      Throwable t = e;
                      Throwable cause = t.getCause();
                      while (cause != null) {
                          t = cause;
                          cause = t.getCause();
                      }
                      String message = "Error processing system messages for " +
                                                                      host;
                      // A remote IOException usually means the messages script
                      // is not available for the target OS. We want to log
                      // at a lower level.
                      if (t instanceof IOException)
                          logger.log(Level.FINE, message, t);
                      else
                          logger.log(Level.WARNING, message, t);
                  } catch (Exception e) {
                      logger.log(Level.WARNING,
                              "Error collecting system messages from " + host,
                              e);
                  }
              }
          }
      }
  }
  