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 com.cloud.storage.resource;
  
  import java.util.List;
  
  import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
  import org.apache.cloudstack.storage.resource.SecondaryStorageResourceHandler;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import com.cloud.agent.api.Answer;
  import com.cloud.agent.api.BackupSnapshotCommand;
  import com.cloud.agent.api.Command;
  import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
  import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
  import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
  import com.cloud.agent.api.storage.CopyVolumeCommand;
  import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
  import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
  import com.cloud.hypervisor.vmware.manager.VmwareHostService;
  import com.cloud.hypervisor.vmware.manager.VmwareStorageManager;
  import com.cloud.hypervisor.vmware.manager.VmwareStorageManagerImpl;
  import com.cloud.hypervisor.vmware.manager.VmwareStorageMount;
  import com.cloud.hypervisor.vmware.mo.ClusterMO;
  import com.cloud.hypervisor.vmware.mo.DatastoreMO;
  import com.cloud.hypervisor.vmware.mo.HostMO;
  import com.cloud.hypervisor.vmware.mo.VmwareHostType;
  import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
  import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHostNetworkSummary;
  import com.cloud.hypervisor.vmware.util.VmwareContext;
  import com.cloud.hypervisor.vmware.util.VmwareHelper;
  import com.cloud.serializer.GsonHelper;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.Pair;
  import com.cloud.utils.StringUtils;
  import com.google.gson.Gson;
  import com.vmware.vim25.ManagedObjectReference;
  import org.apache.logging.log4j.ThreadContext;
  
  public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageResourceHandler, VmwareHostService, VmwareStorageMount {
      protected Logger logger = LogManager.getLogger(getClass());
  
      private final PremiumSecondaryStorageResource _resource;
      private final VmwareStorageManager _storageMgr;
  
      private final Gson _gson;
      private final StorageSubsystemCommandHandler storageSubsystemHandler;
  
      private static ThreadLocal<VmwareContext> currentContext = new ThreadLocal<VmwareContext>();
  
      /*
       * private Map<String, HostMO> _activeHosts = new HashMap<String, HostMO>();
       */
  
      public VmwareSecondaryStorageResourceHandler(PremiumSecondaryStorageResource resource, String nfsVersion) {
          _resource = resource;
          _storageMgr = new VmwareStorageManagerImpl(this, nfsVersion);
          _gson = GsonHelper.getGsonLogger();
  
          VmwareStorageProcessor storageProcessor = new VmwareStorageProcessor(this, true, this, resource.getTimeout(), null, null, _resource, nfsVersion);
          VmwareStorageSubsystemCommandHandler vmwareStorageSubsystemCommandHandler = new VmwareStorageSubsystemCommandHandler(storageProcessor, nfsVersion);
          vmwareStorageSubsystemCommandHandler.setStorageResource(_resource);
          vmwareStorageSubsystemCommandHandler.setStorageManager(_storageMgr);
          storageSubsystemHandler = vmwareStorageSubsystemCommandHandler;
      }
  
      private static String getCommandLogTitle(Command cmd) {
          StringBuffer sb = new StringBuffer();
          if (cmd.getContextParam("job") != null) {
              sb.append(cmd.getContextParam("job"));
          }
  
          sb.append(", cmd: ").append(cmd.getClass().getSimpleName());
  
          return sb.toString();
      }
  
      @Override
      public Answer executeRequest(Command cmd) {
  
          try {
              Answer answer;
              ThreadContext.push(getCommandLogTitle(cmd));
              logCommand(cmd);
  
              if (cmd instanceof PrimaryStorageDownloadCommand) {
                  answer = execute((PrimaryStorageDownloadCommand)cmd);
              } else if (cmd instanceof BackupSnapshotCommand) {
                  answer = execute((BackupSnapshotCommand)cmd);
              } else if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) {
                  answer = execute((CreatePrivateTemplateFromVolumeCommand)cmd);
              } else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
                  answer = execute((CreatePrivateTemplateFromSnapshotCommand)cmd);
              } else if (cmd instanceof CopyVolumeCommand) {
                  answer = execute((CopyVolumeCommand)cmd);
              } else if (cmd instanceof CreateVolumeFromSnapshotCommand) {
                  answer = execute((CreateVolumeFromSnapshotCommand)cmd);
              } else if (cmd instanceof StorageSubSystemCommand) {
                  answer = storageSubsystemHandler.handleStorageCommands((StorageSubSystemCommand)cmd);
              } else if (cmd instanceof CreateEntityDownloadURLCommand) {
                  answer = execute((CreateEntityDownloadURLCommand)cmd);
              } else {
                  answer = _resource.defaultAction(cmd);
              }
  
              // special handling to pass-back context info for cleanups
              if (cmd.getContextParam("execid") != null) {
                  answer.setContextParam("execid", cmd.getContextParam("execid"));
              }
  
              if (cmd.getContextParam("checkpoint") != null) {
                  answer.setContextParam("checkpoint", cmd.getContextParam("checkpoint"));
              }
  
              if (cmd.getContextParam("checkpoint2") != null) {
                  answer.setContextParam("checkpoint2", cmd.getContextParam("checkpoint2"));
              }
  
              if (logger.isDebugEnabled())
                  logger.debug("Command execution answer: " + _gson.toJson(answer));
  
              return answer;
          } finally {
              if (logger.isDebugEnabled())
                  logger.debug("Done executing " + _gson.toJson(cmd));
              recycleServiceContext();
              ThreadContext.pop();
          }
      }
  
      private void logCommand(Command cmd) {
          try {
              logger.debug(String.format("Executing command: [%s].", _gson.toJson(cmd)));
          } catch (Exception e) {
              logger.debug(String.format("Executing command: [%s].", cmd.getClass().getSimpleName()));
          }
      }
  
      protected Answer execute(CreateEntityDownloadURLCommand cmd) {
          boolean success = _storageMgr.execute(this, cmd);
          if (success) {
              return _resource.defaultAction(cmd);
          }
          return new Answer(cmd, false, "Failed to download");
      }
  
      private Answer execute(PrimaryStorageDownloadCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      private Answer execute(BackupSnapshotCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      private Answer execute(CreatePrivateTemplateFromVolumeCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      private Answer execute(CreatePrivateTemplateFromSnapshotCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      private Answer execute(CopyVolumeCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      private Answer execute(CreateVolumeFromSnapshotCommand cmd) {
          return _storageMgr.execute(this, cmd);
      }
  
      @Override
      public VmwareContext getServiceContext(Command cmd) {
          String guid = cmd.getContextParam("guid");
          if (guid == null || guid.isEmpty()) {
              logger.error("Invalid command context parameter guid");
              return null;
          }
  
          String username = cmd.getContextParam("username");
          if (username == null || username.isEmpty()) {
              logger.error("Invalid command context parameter username");
              return null;
          }
  
          String password = cmd.getContextParam("password");
  
          // validate command guid parameter
          String[] tokens = guid.split("@");
          if (tokens == null || tokens.length != 2) {
              logger.error("Invalid content in command context parameter guid");
              return null;
          }
  
          String vCenterAddress = tokens[1];
          String[] hostTokens = tokens[0].split(":");
          if (hostTokens == null || hostTokens.length != 2) {
              logger.error("Invalid content in command context parameter guid");
              return null;
          }
  
          int vCenterSessionTimeout = NumbersUtil.parseInt(cmd.getContextParam("vCenterSessionTimeout"), 1200000);
  
          try {
              _resource.ensureOutgoingRuleForAddress(vCenterAddress);
  
              VmwareContext context = currentContext.get();
              if (context != null && !context.validate()) {
                  invalidateServiceContext(context);
                  context = null;
              }
              if (context == null) {
                  logger.info("Open new VmwareContext. vCenter: " + vCenterAddress + ", user: " + username + ", password: " + StringUtils.getMaskedPasswordForDisplay(password));
                  VmwareSecondaryStorageContextFactory.setVcenterSessionTimeout(vCenterSessionTimeout);
                  context = VmwareSecondaryStorageContextFactory.getContext(vCenterAddress, username, password);
              }
              if (context != null) {
                  context.registerStockObject("serviceconsole", cmd.getContextParam("serviceconsole"));
                  context.registerStockObject("manageportgroup", cmd.getContextParam("manageportgroup"));
                  context.registerStockObject("noderuninfo", cmd.getContextParam("noderuninfo"));
              }
              currentContext.set(context);
              return context;
          } catch (Exception e) {
              logger.error("Unexpected exception " + e.toString(), e);
              return null;
          }
      }
  
      public void recycleServiceContext() {
          if (currentContext.get() != null) {
              VmwareContext context = currentContext.get();
              currentContext.set(null);
              assert (context.getPool() != null);
              context.getPool().registerContext(context);
          }
      }
  
      @Override
      public void invalidateServiceContext(VmwareContext context) {
          currentContext.set(null);
          VmwareSecondaryStorageContextFactory.invalidate(context);
      }
  
      @Override
      public VmwareHypervisorHost getHyperHost(VmwareContext context, Command cmd) {
          String guid = cmd.getContextParam("guid");
          assert (guid != null);
  
          String[] tokens = guid.split("@");
          assert (tokens != null && tokens.length == 2);
  
          ManagedObjectReference morHyperHost = new ManagedObjectReference();
          String[] hostTokens = tokens[0].split(":");
          if (hostTokens == null || hostTokens.length != 2) {
              logger.error("Invalid content in command context parameter guid");
              return null;
          }
  
          morHyperHost.setType(hostTokens[0]);
          morHyperHost.setValue(hostTokens[1]);
  
          if (morHyperHost.getType().equalsIgnoreCase("HostSystem")) {
              HostMO hostMo = new HostMO(context, morHyperHost);
  
              try {
  
                  ManagedObjectReference mor = hostMo.getHyperHostCluster();
                  ClusterMO clusterMo = new ClusterMO(hostMo.getContext(), mor);
                  List<Pair<ManagedObjectReference, String>> hostsInCluster = clusterMo.getClusterHosts();
                  for (Pair<ManagedObjectReference, String> hostPair : hostsInCluster) {
                      HostMO hostIteratorMo = new HostMO(hostMo.getContext(), hostPair.first());
  
                      VmwareHypervisorHostNetworkSummary netSummary =
                          hostIteratorMo.getHyperHostNetworkSummary(hostIteratorMo.getHostType() == VmwareHostType.ESXi ? cmd.getContextParam("manageportgroup")
                              : cmd.getContextParam("serviceconsole"));
                      _resource.ensureOutgoingRuleForAddress(netSummary.getHostIp());
  
                      logger.info("Setup firewall rule for host: " + netSummary.getHostIp());
                  }
              } catch (Throwable e) {
                  logger.warn("Unable to retrive host network information due to exception " + e.toString() + ", host: " + hostTokens[0] + "-" + hostTokens[1]);
              }
  
              return hostMo;
          }
  
          assert (false);
          return new ClusterMO(context, morHyperHost);
      }
  
      @Override
      public String getWorkerName(VmwareContext context, Command cmd, int workerSequence, DatastoreMO dsMo) {
          assert (cmd.getContextParam("worker") != null);
          assert (workerSequence < 2);
  
          if (workerSequence == 0)
              return cmd.getContextParam("worker");
          return cmd.getContextParam("worker2");
      }
  
      @Override
      public String getMountPoint(String storageUrl, String nfsVersion) {
          return _resource.getRootDir(storageUrl, nfsVersion);
      }
  
      @Override
      public String createLogMessageException(Throwable e, Command command) {
          String message = String.format("%s failed due to [%s].", command.getClass().getSimpleName(), VmwareHelper.getExceptionMessage(e));
          logger.error(message, e);
  
          return message;
      }
  
      @Override
      public VmwareStorageProcessor getStorageProcessor() {
          return null;
      }
  }
  