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.nestedcloudstack;
  
  import br.com.autonomiccs.apacheCloudStack.client.ApacheCloudStackClient;
  import br.com.autonomiccs.apacheCloudStack.client.ApacheCloudStackRequest;
  import br.com.autonomiccs.apacheCloudStack.client.beans.ApacheCloudStackUser;
  import br.com.autonomiccs.apacheCloudStack.exceptions.ApacheCloudStackClientRequestRuntimeException;
  import com.cloud.utils.component.AdapterBase;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.fasterxml.jackson.databind.ObjectMapper;
  import org.apache.commons.lang3.StringUtils;
  import com.google.common.collect.ImmutableMap;
  import org.apache.cloudstack.outofbandmanagement.OutOfBandManagement;
  import org.apache.cloudstack.outofbandmanagement.OutOfBandManagementDriver;
  import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverChangePasswordCommand;
  import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverCommand;
  import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverPowerCommand;
  import org.apache.cloudstack.outofbandmanagement.driver.OutOfBandManagementDriverResponse;
  
  import java.io.IOException;
  import java.util.List;
  import java.util.Map;
  
  public final class NestedCloudStackOutOfBandManagementDriver extends AdapterBase implements OutOfBandManagementDriver {
  
      public OutOfBandManagementDriverResponse execute(final OutOfBandManagementDriverCommand cmd) {
          OutOfBandManagementDriverResponse response = new OutOfBandManagementDriverResponse(null, "Unsupported Command", false);
  
          if (cmd instanceof OutOfBandManagementDriverPowerCommand) {
              response = execute((OutOfBandManagementDriverPowerCommand) cmd);
          } else if (cmd instanceof OutOfBandManagementDriverChangePasswordCommand) {
              throw new CloudRuntimeException("Change password operation is not supported by the nested-cloudstack out-of-band management driver");
          }
  
          return response;
      }
  
      protected void ensureOptionExists(final ImmutableMap<OutOfBandManagement.Option, String> options, final OutOfBandManagement.Option option) {
          if (options != null && option != null && options.containsKey(option) && StringUtils.isNotEmpty(options.get(option))) {
              return;
          }
          throw new CloudRuntimeException("Invalid out-of-band management configuration detected for the nested-cloudstack driver");
      }
  
      protected OutOfBandManagement.PowerState getNestedVMPowerState(final String jsonResponse) {
          if (StringUtils.isEmpty(jsonResponse)) {
              return OutOfBandManagement.PowerState.Unknown;
          }
  
          final ObjectMapper mapper = new ObjectMapper();
          try {
              Map<String, Object> listResponse = mapper.readValue(jsonResponse, Map.class);
              if (listResponse != null && listResponse.containsKey("listvirtualmachinesresponse")
                      && ((Map<String, Object>) listResponse.get("listvirtualmachinesresponse")).containsKey("virtualmachine")) {
                  Map<String, String> vmResponse = ((Map<String, List<Map<String, String>>>) listResponse.get("listvirtualmachinesresponse")).get("virtualmachine").get(0);
                  if (vmResponse != null && vmResponse.containsKey("state")) {
                      if("Running".equals(vmResponse.get("state"))) {
                          return OutOfBandManagement.PowerState.On;
                      } else if("Stopped".equals(vmResponse.get("state"))) {
                          return OutOfBandManagement.PowerState.Off;
                      }
                  }
              }
          } catch (IOException e) {
              logger.warn("Exception caught while de-serializing and reading state of the nested-cloudstack VM from the response: " + jsonResponse + ", with exception:", e);
          }
          return OutOfBandManagement.PowerState.Unknown;
      }
  
      private OutOfBandManagementDriverResponse execute(final OutOfBandManagementDriverPowerCommand cmd) {
          if (cmd == null || cmd.getPowerOperation() == null) {
              throw new CloudRuntimeException("Invalid out-of-band management power command provided to the nested-cloudstack driver");
          }
  
          final ImmutableMap<OutOfBandManagement.Option, String> options = cmd.getOptions();
          ensureOptionExists(options, OutOfBandManagement.Option.ADDRESS);
          ensureOptionExists(options, OutOfBandManagement.Option.PORT);
          ensureOptionExists(options, OutOfBandManagement.Option.USERNAME);
          ensureOptionExists(options, OutOfBandManagement.Option.PASSWORD);
  
          final String url = options.get(OutOfBandManagement.Option.ADDRESS);
          final String vmUuid = options.get(OutOfBandManagement.Option.PORT);
          final String apiKey = options.get(OutOfBandManagement.Option.USERNAME);
          final String secretKey = options.get(OutOfBandManagement.Option.PASSWORD);
  
          final ApacheCloudStackUser apacheCloudStackUser = new ApacheCloudStackUser(secretKey, apiKey);
          final ApacheCloudStackClient client = new ApacheCloudStackClient(url, apacheCloudStackUser);
          client.setValidateServerHttpsCertificate(false);
          client.setShouldRequestsExpire(false);
          client.setConnectionTimeout((int) cmd.getTimeout().getStandardSeconds());
  
          String apiName = "listVirtualMachines";
          switch (cmd.getPowerOperation()) {
              case ON:
                  apiName = "startVirtualMachine";
                  break;
              case OFF:
              case SOFT:
                  apiName = "stopVirtualMachine";
                  break;
              case CYCLE:
              case RESET:
                  apiName = "rebootVirtualMachine";
                  break;
          }
  
          final ApacheCloudStackRequest apacheCloudStackRequest = new ApacheCloudStackRequest(apiName);
          apacheCloudStackRequest.addParameter("response", "json");
          apacheCloudStackRequest.addParameter("forced", "true");
          apacheCloudStackRequest.addParameter("id", vmUuid);
  
          final String apiResponse;
          try {
              apiResponse = client.executeRequest(apacheCloudStackRequest);
          } catch (final ApacheCloudStackClientRequestRuntimeException e) {
              logger.error("Nested CloudStack oobm plugin failed due to API error: ", e);
              final OutOfBandManagementDriverResponse failedResponse = new OutOfBandManagementDriverResponse(e.getResponse(), "HTTP error code: " + e.getStatusCode(), false);
              if (e.getStatusCode() == 401) {
                  failedResponse.setAuthFailure(true);
              }
              return failedResponse;
          }
  
          final OutOfBandManagementDriverResponse response = new OutOfBandManagementDriverResponse(apiResponse, null, true);
          if (OutOfBandManagement.PowerOperation.STATUS.equals(cmd.getPowerOperation())) {
              response.setPowerState(getNestedVMPowerState(apiResponse));
          }
          return response;
      }
  }
  