/**
   * Filter a list of VolumeDescriptors by type(s).
   *
   * @param descriptors -- Original list.
   * @param inclusive -- Types to be included (or null if not used).
   * @param exclusive -- Types to be excluded (or null if not used).
   * @return List<VolumeDescriptor>
   */
  public static List<VolumeDescriptor> filterByType(
      List<VolumeDescriptor> descriptors, Type[] inclusive, Type[] exclusive) {
    List<VolumeDescriptor> result = new ArrayList<VolumeDescriptor>();
    if (descriptors == null) {
      return result;
    }

    HashSet<Type> included = new HashSet<Type>();
    if (inclusive != null) {
      included.addAll(Arrays.asList(inclusive));
    }
    HashSet<Type> excluded = new HashSet<Type>();
    if (exclusive != null) {
      excluded.addAll(Arrays.asList(exclusive));
    }
    for (VolumeDescriptor desc : descriptors) {
      if (excluded.contains(desc.getType())) {
        continue;
      }
      if (included.isEmpty() || included.contains(desc.getType())) {
        result.add(desc);
      }
    }
    return result;
  }
 /**
  * Helper method to retrieve the change vpool volume hiding in the volume descriptors and to find
  * the new vpool.
  *
  * @param descriptors list of volumes
  * @return Map<URI,URI> of the vpool change volume and the old vpool associated to it.
  */
 public static Map<URI, URI> createVolumeToNewVpoolMap(List<VolumeDescriptor> descriptors) {
   Map<URI, URI> volumesToNewVpoolMap = new HashMap<URI, URI>();
   if (descriptors != null) {
     for (VolumeDescriptor volumeDescriptor : descriptors) {
       if (volumeDescriptor.getParameters() != null) {
         if (volumeDescriptor
                 .getParameters()
                 .get(VolumeDescriptor.PARAM_VPOOL_CHANGE_EXISTING_VOLUME_ID)
             != null) {
           URI volumeURI =
               (URI)
                   volumeDescriptor
                       .getParameters()
                       .get(VolumeDescriptor.PARAM_VPOOL_CHANGE_EXISTING_VOLUME_ID);
           URI newVpoolURI =
               (URI)
                   volumeDescriptor
                       .getParameters()
                       .get(VolumeDescriptor.PARAM_VPOOL_CHANGE_NEW_VPOOL_ID);
           volumesToNewVpoolMap.put(volumeURI, newVpoolURI);
         }
       }
     }
   }
   return volumesToNewVpoolMap;
 }
 /**
  * Return a List of URIs for the volumes.
  *
  * @param descriptors List<VolumeDescriptors>
  * @return List<URI> of volumes in the input list
  */
 public static List<URI> getVolumeURIs(List<VolumeDescriptor> descriptors) {
   Set<URI> volumeURIs = new HashSet<>();
   for (VolumeDescriptor desc : descriptors) {
     volumeURIs.add(desc.getVolumeURI());
   }
   List<URI> volumeList = new ArrayList<>();
   volumeList.addAll(volumeURIs);
   return volumeList;
 }
 /**
  * Return a map of pool URI to a list of descriptors in that pool.
  *
  * @param descriptors List<VolumeDescriptors>
  * @return Map of pool URI to List<VolumeDescriptors> in that pool
  */
 public static Map<URI, List<VolumeDescriptor>> getPoolMap(List<VolumeDescriptor> descriptors) {
   HashMap<URI, List<VolumeDescriptor>> poolMap = new HashMap<URI, List<VolumeDescriptor>>();
   for (VolumeDescriptor desc : descriptors) {
     if (poolMap.get(desc.getPoolURI()) == null) {
       poolMap.put(desc.getPoolURI(), new ArrayList<VolumeDescriptor>());
     }
     poolMap.get(desc.getPoolURI()).add(desc);
   }
   return poolMap;
 }
 /**
  * Returns all the descriptors of a given type.
  *
  * @param descriptors List<VolumeDescriptor> input list
  * @param type enum Type
  * @return returns list elements matching given type
  */
 public static List<VolumeDescriptor> getDescriptors(
     List<VolumeDescriptor> descriptors, Type type) {
   List<VolumeDescriptor> list = new ArrayList<VolumeDescriptor>();
   for (VolumeDescriptor descriptor : descriptors) {
     if (descriptor.getType() == type) {
       list.add(descriptor);
     }
   }
   return list;
 }
  private void handleBlockVolumeErrors(DbClient dbClient) {
    for (VolumeDescriptor volumeDescriptor :
        VolumeDescriptor.getDescriptors(_volumeDescriptors, VolumeDescriptor.Type.BLOCK_DATA)) {

      Volume volume = dbClient.queryObject(Volume.class, volumeDescriptor.getVolumeURI());

      if (volume != null && (volume.getNativeId() == null || volume.getNativeId().equals(""))) {
        _log.info("No native id was present on volume {}, marking inactive", volume.getLabel());
        dbClient.markForDeletion(volume);
      }
    }
  }
 /**
  * Helper that checks any boolean property in the volume descriptor. False is default response.
  *
  * @param descriptors descriptor list
  * @param param string of parameter to check
  * @return true if the property is set and true, false otherwise
  */
 private static boolean isPropertyEnabled(List<VolumeDescriptor> descriptors, String param) {
   if (descriptors != null) {
     for (VolumeDescriptor volumeDescriptor : descriptors) {
       if (volumeDescriptor.getParameters() != null) {
         if (volumeDescriptor.getParameters().get(param) != null) {
           return (Boolean) volumeDescriptor.getParameters().get(param);
         }
       }
     }
   }
   return false;
 }
  private void handleVplexVolumeErrors(DbClient dbClient) {

    List<String> finalMessages = new ArrayList<String>();

    for (VolumeDescriptor volumeDescriptor :
        VolumeDescriptor.getDescriptors(
            _volumeDescriptors, VolumeDescriptor.Type.VPLEX_VIRT_VOLUME)) {

      Volume volume = dbClient.queryObject(Volume.class, volumeDescriptor.getVolumeURI());

      _log.info("Looking at VPLEX virtual volume {}", volume.getLabel());

      boolean deactivateVirtualVolume = true;
      List<String> livingVolumeNames = new ArrayList<String>();

      _log.info("Its associated volumes are: " + volume.getAssociatedVolumes());
      for (String associatedVolumeUri : volume.getAssociatedVolumes()) {
        Volume associatedVolume =
            dbClient.queryObject(Volume.class, URI.create(associatedVolumeUri));
        if (associatedVolume != null && !associatedVolume.getInactive()) {
          _log.warn(
              "VPLEX virtual volume {} has active associated volume {}",
              volume.getLabel(),
              associatedVolume.getLabel());
          livingVolumeNames.add(associatedVolume.getLabel());
          deactivateVirtualVolume = false;
        }
      }

      if (deactivateVirtualVolume) {
        _log.info(
            "VPLEX virtual volume {} has no active associated volumes, marking for deletion",
            volume.getLabel());
        dbClient.markForDeletion(volume);
      } else {
        String message =
            "VPLEX virtual volume "
                + volume.getLabel()
                + " will not be marked for deletion "
                + "because it still has active associated volumes (";
        message += Joiner.on(",").join(livingVolumeNames) + ")";
        finalMessages.add(message);
        _log.warn(message);
      }
    }

    if (!finalMessages.isEmpty()) {
      String finalMessage = Joiner.on("; ").join(finalMessages) + ".";
      _log.error(finalMessage);
    }
  }
 /**
  * Returns all descriptors that have the PARAM_DO_NOT_DELETE_VOLUME flag set to true.
  *
  * @param descriptors List of descriptors to check
  * @return all descriptors that have the PARAM_DO_NOT_DELETE_VOLUME flag set to true
  */
 public static List<VolumeDescriptor> getDoNotDeleteDescriptors(
     List<VolumeDescriptor> descriptors) {
   List<VolumeDescriptor> doNotDeleteDescriptors = new ArrayList<VolumeDescriptor>();
   if (descriptors != null && !descriptors.isEmpty()) {
     for (VolumeDescriptor descriptor : descriptors) {
       if (descriptor.getParameters() != null
           && descriptor.getParameters().get(VolumeDescriptor.PARAM_DO_NOT_DELETE_VOLUME) != null
           && descriptor
               .getParameters()
               .get(VolumeDescriptor.PARAM_DO_NOT_DELETE_VOLUME)
               .equals(Boolean.TRUE)) {
         doNotDeleteDescriptors.add(descriptor);
       }
     }
   }
   return doNotDeleteDescriptors;
 }
 /**
  * Helper method to retrieve the vpool change volume hiding in the volume descriptors
  *
  * @param descriptors list of volumes
  * @return URI of the vpool change volume
  */
 public static URI getVirtualPoolChangeVolume(List<VolumeDescriptor> descriptors) {
   if (descriptors != null) {
     for (VolumeDescriptor volumeDescriptor : descriptors) {
       if (volumeDescriptor.getParameters() != null) {
         if ((URI)
                 volumeDescriptor
                     .getParameters()
                     .get(VolumeDescriptor.PARAM_VPOOL_CHANGE_VOLUME_ID)
             != null) {
           return (URI)
               volumeDescriptor.getParameters().get(VolumeDescriptor.PARAM_VPOOL_CHANGE_VOLUME_ID);
         }
       }
     }
   }
   return null;
 }
  /**
   * Return a map of pool URI to a list of descriptors in that pool of each size.
   *
   * @param descriptors List<VolumeDescriptors>
   * @return Map of pool URI to a map of identical sized volumes to List<VolumeDescriptors> in that
   *     pool of that size
   */
  public static Map<URI, Map<Long, List<VolumeDescriptor>>> getPoolSizeMap(
      List<VolumeDescriptor> descriptors) {
    Map<URI, Map<Long, List<VolumeDescriptor>>> poolSizeMap =
        new HashMap<URI, Map<Long, List<VolumeDescriptor>>>();
    for (VolumeDescriptor desc : descriptors) {

      // If the outside pool map doesn't exist, create it.
      if (poolSizeMap.get(desc._poolURI) == null) {
        poolSizeMap.put(desc._poolURI, new HashMap<Long, List<VolumeDescriptor>>());
      }

      // If the inside size map doesn't exist, create it.
      if (poolSizeMap.get(desc._poolURI).get(desc.getVolumeSize()) == null) {
        poolSizeMap.get(desc._poolURI).put(desc.getVolumeSize(), new ArrayList<VolumeDescriptor>());
      }

      // Add volume to the list
      poolSizeMap.get(desc._poolURI).get(desc.getVolumeSize()).add(desc);
    }

    return poolSizeMap;
  }
 /**
  * Helper method to retrieve the source vpool change volume hiding in the volume descriptors.
  * Needed primarily for rollback as the old vpool URI will be in the map: Map<Volume URI, Old
  * Vpool URI>
  *
  * @param descriptors list of volumes
  * @return Map<URI,URI> of the vpool change volume and the old vpool associated to it.
  */
 public static Map<URI, URI> getAllVirtualPoolChangeSourceVolumes(
     List<VolumeDescriptor> descriptors) {
   Map<URI, URI> sourceVolumes = new HashMap<URI, URI>();
   if (descriptors != null) {
     for (VolumeDescriptor volumeDescriptor : descriptors) {
       if (volumeDescriptor.getParameters() != null) {
         if ((URI) volumeDescriptor.getParameters().get(VolumeDescriptor.PARAM_VPOOL_OLD_VPOOL_ID)
             != null) {
           URI volumeURI =
               (URI)
                   volumeDescriptor
                       .getParameters()
                       .get(VolumeDescriptor.PARAM_VPOOL_CHANGE_VOLUME_ID);
           URI oldVpoolURI =
               (URI)
                   volumeDescriptor.getParameters().get(VolumeDescriptor.PARAM_VPOOL_OLD_VPOOL_ID);
           sourceVolumes.put(volumeURI, oldVpoolURI);
         }
       }
     }
   }
   return sourceVolumes;
 }
  /** {@inheritDoc} */
  @Override
  public TaskList create(
      List<BlockObject> fcSourceObjList,
      VirtualArray varray,
      String name,
      boolean createInactive,
      int count,
      String taskId) {

    // Populate the descriptors list with all volumes required
    // to create the VPLEX volume copies.
    int sourceCounter = 0;
    URI vplexSrcSystemId = null;
    TaskList taskList = new TaskList();
    List<Volume> vplexCopyVolumes = new ArrayList<Volume>();
    List<VolumeDescriptor> volumeDescriptors = new ArrayList<VolumeDescriptor>();
    List<BlockObject> sortedSourceObjectList = sortFullCopySourceList(fcSourceObjList);
    for (BlockObject fcSourceObj : sortedSourceObjectList) {
      URI fcSourceURI = fcSourceObj.getId();
      if (URIUtil.isType(fcSourceURI, BlockSnapshot.class)) {
        // Full copy of snapshots is not supported for VPLEX.
        return super.create(sortedSourceObjectList, varray, name, createInactive, count, taskId);
      }

      Volume vplexSrcVolume = (Volume) fcSourceObj;
      String copyName = name + (sortedSourceObjectList.size() > 1 ? "-" + ++sourceCounter : "");

      // Create a volume descriptor for the source VPLEX volume being copied.
      // and add it to the descriptors list. Be sure to identify this VPLEX
      // volume as the source volume being copied.
      vplexSrcSystemId = fcSourceObj.getStorageController();
      VolumeDescriptor vplexSrcVolumeDescr =
          new VolumeDescriptor(
              VolumeDescriptor.Type.VPLEX_VIRT_VOLUME, vplexSrcSystemId, fcSourceURI, null, null);
      Map<String, Object> descrParams = new HashMap<String, Object>();
      descrParams.put(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID, Boolean.TRUE);
      vplexSrcVolumeDescr.setParameters(descrParams);
      volumeDescriptors.add(vplexSrcVolumeDescr);

      // Get some info about the VPLEX volume being copied and its storage
      // system.
      Project vplexSrcProject =
          BlockFullCopyUtils.queryFullCopySourceProject(fcSourceObj, _dbClient);
      StorageSystem vplexSrcSystem = _dbClient.queryObject(StorageSystem.class, vplexSrcSystemId);
      Project vplexSystemProject =
          VPlexBlockServiceApiImpl.getVplexProject(vplexSrcSystem, _dbClient, _tenantsService);

      // For the VPLEX volume being copied, determine which of the associated
      // backend volumes is the primary and, for distributed volumes, which
      // is the HA volume. The primary volume will be natively copied and we
      // we need to place and prepare a volume to hold the copy. This copy
      // will be the primary backend volume for the VPLEX volume copy. For
      // a distributed virtual volume, we will need to place and prepare
      // a volume to hold the HA volume of the VPLEX volume copy.
      Volume vplexSrcPrimaryVolume = null;
      Volume vplexSrcHAVolume = null;
      StringSet assocVolumeURIs = vplexSrcVolume.getAssociatedVolumes();
      Iterator<String> assocVolumeURIsIter = assocVolumeURIs.iterator();
      while (assocVolumeURIsIter.hasNext()) {
        URI assocVolumeURI = URI.create(assocVolumeURIsIter.next());
        Volume assocVolume = _dbClient.queryObject(Volume.class, assocVolumeURI);
        if (assocVolume.getVirtualArray().toString().equals(varray.getId().toString())) {
          vplexSrcPrimaryVolume = assocVolume;
        } else {
          vplexSrcHAVolume = assocVolume;
        }
      }

      // Get the capabilities
      VirtualPool vpool = BlockFullCopyUtils.queryFullCopySourceVPool(fcSourceObj, _dbClient);
      VirtualPoolCapabilityValuesWrapper capabilities =
          getCapabilitiesForFullCopyCreate(fcSourceObj, vpool, count);

      // Get the number of copies to create and the size of the volumes.
      // Note that for the size, we must use the actual provisioned size
      // of the source side backend volume. The size passed in the
      // capabilities will be the size of the VPLEX volume. When the
      // source side backend volume for the copy is provisioned, you
      // might not get that actual size. On VMAX, the size will be slightly
      // larger while for VNX the size will be exactly what is requested.
      // So, if the source side is a VMAX, the source side for the copy
      // will be slightly larger than the size in the capabilities. If the HA
      // side is VNX and we use the size in the capabilities, then you will
      // get exactly that size for the HA backend volume. As a result, source
      // side backend volume for the copy will be slightly larger than the
      // HA side. Now the way a VPLEX copy is made is it uses native full
      // copy to create a native full copy of the source side backend
      // volume. It then provisions the HA side volume. The new source side
      // backend copy is then imported into VPLEX in the same way as is done
      // for a vpool change that imports a volume to VPLEX. This code in the
      // VPLEX controller creates a local VPLEX volume using the source side
      // copy and for a distributed volume it then attaches as a remote
      // mirror the HA backend volume that is provisioned. If the HA volume
      // is slightly smaller, then this will fail on the VPLEX. So, we must
      // ensure that HA side volume is big enough by using the provisioned
      // capacity of the source side backend volume of the VPLEX volume being
      // copied.
      long size = vplexSrcPrimaryVolume.getProvisionedCapacity();

      // Place and prepare a volume for each copy to serve as a native
      // copy of a VPLEX backend volume. The VPLEX backend volume that
      // is copied is the backend volume in the same virtual array as the
      // VPLEX volume i.e, the primary backend volume. Create
      // descriptors for these prepared volumes and add them to the list.
      List<Volume> vplexCopyPrimaryVolumes =
          prepareFullCopyPrimaryVolumes(
              copyName, count, vplexSrcPrimaryVolume, capabilities, volumeDescriptors);

      // If the VPLEX volume being copied is distributed, then the VPLEX
      // HA volume should be non-null. We use the VPLEX scheduler to place
      // and then prepare volumes for the HA volumes of the VPLEX volume
      // copies. This should be done in the same manner as is done for the
      // import volume routine. This is because to form the VPLEX volume
      // copy we import the copy of the primary backend volume.
      List<Volume> vplexCopyHAVolumes = new ArrayList<Volume>();
      if (vplexSrcHAVolume != null) {
        vplexCopyHAVolumes.addAll(
            prepareFullCopyHAVolumes(
                copyName,
                count,
                size,
                vplexSrcSystem,
                vplexSystemProject,
                varray,
                vplexSrcHAVolume,
                taskId,
                volumeDescriptors));
      }

      // For each copy to be created, place and prepare a volume for the
      // primary backend volume copy. When copying a distributed VPLEX
      // volume, we also must place and prepare a volume for the HA
      // backend volume copy. Lastly, we must prepare a volume for the
      // VPLEX volume copy. Create descriptors for these prepared volumes
      // and add them to the volume descriptors list.
      for (int i = 0; i < count; i++) {
        // Prepare a new VPLEX volume for each copy.
        Volume vplexCopyPrimaryVolume = vplexCopyPrimaryVolumes.get(i);
        Volume vplexCopyHAVolume = null;
        if (vplexCopyHAVolumes.size() != 0) {
          vplexCopyHAVolume = vplexCopyHAVolumes.get(i);
        }
        Volume vplexCopyVolume =
            prepareFullCopyVPlexVolume(
                copyName,
                count,
                i,
                size,
                vplexSrcVolume,
                vplexSrcProject,
                varray,
                vpool,
                vplexSrcSystemId,
                vplexCopyPrimaryVolume,
                vplexCopyHAVolume,
                taskId,
                volumeDescriptors);
        vplexCopyVolumes.add(vplexCopyVolume);

        // Create task for each copy.
        Operation op = vplexCopyVolume.getOpStatus().get(taskId);
        TaskResourceRep task = toTask(vplexCopyVolume, taskId, op);
        taskList.getTaskList().add(task);
      }
    }

    // Invoke the VPLEX controller to create the copies.
    try {
      s_logger.info("Getting VPlex controller {}.", taskId);
      VPlexController controller =
          getController(VPlexController.class, DiscoveredDataObject.Type.vplex.toString());
      // TBD controller needs to be updated to handle CGs.
      controller.createFullCopy(vplexSrcSystemId, volumeDescriptors, taskId);
      s_logger.info("Successfully invoked controller.");
    } catch (InternalException e) {
      s_logger.error("Controller error", e);

      // Update the status for the VPLEX volume copies and their
      // corresponding tasks.
      for (Volume vplexCopyVolume : vplexCopyVolumes) {
        Operation op = vplexCopyVolume.getOpStatus().get(taskId);
        if (op != null) {
          op.error(e);
          vplexCopyVolume.getOpStatus().updateTaskStatus(taskId, op);
          _dbClient.persistObject(vplexCopyVolume);
          for (TaskResourceRep task : taskList.getTaskList()) {
            if (task.getResource().getId().equals(vplexCopyVolume.getId())) {
              task.setState(op.getStatus());
              task.setMessage(op.getMessage());
              break;
            }
          }
        }
      }

      // Mark all volumes inactive, except for the VPLEX volume
      // we were trying to copy.
      for (VolumeDescriptor descriptor : volumeDescriptors) {
        if (descriptor.getParameters().get(VolumeDescriptor.PARAM_IS_COPY_SOURCE_ID) == null) {
          Volume volume = _dbClient.queryObject(Volume.class, descriptor.getVolumeURI());
          volume.setInactive(true);
          _dbClient.persistObject(volume);
        }
      }
    }

    return taskList;
  }