content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  package org.apache.cloudstack.outofbandmanagement.driver.ipmitool;
  
  import com.cloud.utils.exception.CloudRuntimeException;
  import org.apache.commons.lang3.StringUtils;
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableMap;
  import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
  import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverResponse;
  import org.apache.cloudstack.utils.process.ProcessResult;
  import org.apache.cloudstack.utils.process.ProcessRunner;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import org.joda.time.Duration;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.concurrent.ExecutorService;
  
  public final class IpmitoolWrapper {
      Logger logger = LogManager.getLogger(getClass());
  
      private final ProcessRunner RUNNER;
  
      public IpmitoolWrapper(ExecutorService executor) {
          this.RUNNER = new ProcessRunner(executor);
      }
  
      public String parsePowerCommand(OutOfBandManagement.PowerOperation operation) {
          if (operation == null) {
              throw new IllegalStateException("Invalid power operation requested");
          }
          switch (operation) {
              case ON:
              case OFF:
              case CYCLE:
              case RESET:
              case SOFT:
              case STATUS:
                  break;
              default:
                  throw new IllegalStateException("Invalid power operation requested");
          }
          return operation.toString().toLowerCase();
      }
  
      public OutOfBandManagement.PowerState parsePowerState(final String standardOutput) {
          if (StringUtils.isEmpty(standardOutput)) {
              return OutOfBandManagement.PowerState.Unknown;
          }
          if (standardOutput.equals("Chassis Power is on")) {
              return OutOfBandManagement.PowerState.On;
          } else if (standardOutput.equals("Chassis Power is off")) {
              return OutOfBandManagement.PowerState.Off;
          }
          return OutOfBandManagement.PowerState.Unknown;
      }
  
      public List<String> getIpmiToolCommandArgs(final String ipmiToolPath, final String ipmiInterface, final String retries,
                                                        final ImmutableMap<OutOfBandManagement.Option, String> options, String... commands) {
  
          final ImmutableList.Builder<String> ipmiToolCommands = ImmutableList.<String>builder()
                                                              .add(ipmiToolPath)
                                                              .add("-I")
                                                              .add(ipmiInterface)
                                                              .add("-R")
                                                              .add(retries)
                                                              .add("-v");
  
          if (options != null) {
              for (ImmutableMap.Entry<OutOfBandManagement.Option, String> option : options.entrySet()) {
                  switch (option.getKey()) {
                      case ADDRESS:
                          ipmiToolCommands.add("-H");
                          break;
                      case PORT:
                          ipmiToolCommands.add("-p");
                          break;
                      case USERNAME:
                          ipmiToolCommands.add("-U");
                          break;
                      case PASSWORD:
                          ipmiToolCommands.add("-P");
                          break;
                      default:
                          continue;
                  }
                  ipmiToolCommands.add(option.getValue());
              }
          }
          for (String command : commands) {
              ipmiToolCommands.add(command);
          }
          return ipmiToolCommands.build();
      }
  
      /**
       * Expected usersList string contains legends on first line and users on rest
       * ID Name  Callin Link Auth IPMI Msg Channel Priv Limit
       * 1  admin true   true true ADMINISTRATOR
       */
      public String findIpmiUser(final String usersList, final String username) {
  
          // Assuming user 'ID' index on 1st position
          int idIndex = 0;
  
          // Assuming  user 'Name' index on 2nd position
          int usernameIndex = 1;
  
          final String[] lines = usersList.split("\\r?\\n");
          if (lines.length < 2) {
              throw new CloudRuntimeException("Error parsing user ID from ipmi user listing");
          }
          // Find user and name indexes from the 1st line if not on default position
          final String[] legends = lines[0].split(" +");
          for (int idx = 0; idx < legends.length; idx++) {
              if (legends[idx].equals("ID")) {
                  idIndex = idx;
              }
              if (legends[idx].equals("Name")) {
                  usernameIndex = idx;
              }
          }
          // Find user 'ID' based on provided username and ID/Name positions
          String userId = null;
          for (int idx = 1; idx < lines.length; idx++) {
              final String[] words = lines[idx].split(" +");
              if (usernameIndex < words.length && idIndex < words.length) {
                  if (words[usernameIndex].equals(username)) {
                      userId = words[idIndex];
                  }
              }
          }
          return userId;
      }
  
      public OutOfBandManagementDriverResponse executeCommands(final List<String> commands) {
          return executeCommands(commands, ProcessRunner.DEFAULT_MAX_TIMEOUT);
      }
  
      public OutOfBandManagementDriverResponse executeCommands(final List<String> commands, final Duration timeOut) {
          final ProcessResult result = RUNNER.executeCommands(commands, timeOut);
          if (logger.isTraceEnabled()) {
              List<String> cleanedCommands = getSanatisedCommandStrings(commands);
              logger.trace("Executed ipmitool process with commands: {}\nIpmitool execution standard output: {}\nIpmitool execution error output: {}",
                      StringUtils.join(cleanedCommands, ", "),
                      result.getStdOutput(),
                      result.getStdError());
          }
          return new OutOfBandManagementDriverResponse(result.getStdOutput(), result.getStdError(), result.isSuccess());
      }
  
      List<String> getSanatisedCommandStrings(List<String> commands) {
          List<String> cleanedCommands = new ArrayList<>();
          int maskNextCommand = 0;
          for (String command : commands) {
              if (maskNextCommand > 0) {
                  cleanedCommands.add("**** ");
                  maskNextCommand--;
                  continue;
              }
              if (command.equalsIgnoreCase("-P")) {
                  maskNextCommand = 1;
              } else if (command.toLowerCase().endsWith("password")) {
                  maskNextCommand = 2;
              }
              cleanedCommands.add(command);
          }
          return cleanedCommands;
      }
  }
  