/*
* Copyright (c) 2012-2015 iWave Software LLC
* All Rights Reserved
*/
package com.emc.sa.asset.providers;
import static com.emc.vipr.client.core.filters.CompatibilityFilter.INCOMPATIBLE;
import static com.emc.vipr.client.core.filters.DiscoveryStatusFilter.COMPLETE;
import static com.emc.vipr.client.core.filters.RegistrationFilter.REGISTERED;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import com.emc.sa.asset.AssetOptionsContext;
import com.emc.sa.asset.AssetOptionsUtils;
import com.emc.sa.asset.annotation.Asset;
import com.emc.sa.asset.annotation.AssetDependencies;
import com.emc.sa.asset.annotation.AssetNamespace;
import com.emc.sa.asset.providers.BlockProvider.UnexportedBlockResourceFilter;
import com.emc.sa.machinetags.KnownMachineTags;
import com.emc.sa.machinetags.vmware.VMwareDatastoreTagger;
import com.emc.sa.service.vipr.block.BlockStorageUtils;
import com.emc.sa.util.ResourceType;
import com.emc.storageos.db.client.util.NullColumnValueGetter;
import com.emc.storageos.model.BulkIdParam;
import com.emc.storageos.model.RelatedResourceRep;
import com.emc.storageos.model.block.BlockObjectRestRep;
import com.emc.storageos.model.block.VolumeRestRep;
import com.emc.storageos.model.block.export.ExportGroupRestRep;
import com.emc.storageos.model.block.export.ITLRestRep;
import com.emc.storageos.model.host.HostRestRep;
import com.emc.storageos.model.host.cluster.ClusterRestRep;
import com.emc.storageos.model.host.vcenter.VcenterDataCenterRestRep;
import com.emc.storageos.model.host.vcenter.VcenterRestRep;
import com.emc.vipr.client.ViPRCoreClient;
import com.emc.vipr.client.core.filters.BlockVolumeBootVolumeFilter;
import com.emc.vipr.client.core.filters.HostTypeFilter;
import com.emc.vipr.client.core.filters.SourceTargetVolumesFilter;
import com.emc.vipr.model.catalog.AssetOption;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@Component
@AssetNamespace("vipr")
public class VMWareProvider extends BaseHostProvider {
protected List<VcenterRestRep> listVCenters(AssetOptionsContext context) {
return api(context).vcenters().getByTenant(context.getTenant(), REGISTERED.and(INCOMPATIBLE.not()));
}
protected List<VcenterRestRep> listVcentersForCluster(AssetOptionsContext context, URI clusterId) {
ClusterRestRep clusterRestRep = api(context).clusters().get(clusterId);
RelatedResourceRep vcenterDatacenter = clusterRestRep.getVcenterDataCenter();
if (vcenterDatacenter == null) {
// return all vcenters
return api(context).vcenters().getByTenant(context.getTenant(), REGISTERED.and(INCOMPATIBLE.not()));
}
else {
// return the vcenter this vipr cluster is already associated with in vcenter
RelatedResourceRep vcenterRelatedRestRep = api(context).vcenterDataCenters().get(vcenterDatacenter.getId()).getVcenter();
VcenterRestRep vcenterRestRep = api(context).vcenters().get(vcenterRelatedRestRep.getId());
return Arrays.asList(vcenterRestRep);
}
}
protected List<VcenterDataCenterRestRep> listDatacentersByVCenter(AssetOptionsContext context, URI vcenterId) {
return api(context).vcenterDataCenters().getByVcenter(vcenterId, context.getTenant());
}
protected List<VcenterDataCenterRestRep> listDatacentersByVCenterAndCluster(AssetOptionsContext context, URI vcenterId, URI clusterId) {
ClusterRestRep clusterRestRep = api(context).clusters().get(clusterId);
RelatedResourceRep vcenterDatacenter = clusterRestRep.getVcenterDataCenter();
if (vcenterDatacenter == null) {
// return all datacenters for this datacenter
return api(context).vcenterDataCenters().getByVcenter(vcenterId, context.getTenant());
}
else {
// return the datacenter this vipr cluster is already associated with in vcenter
VcenterDataCenterRestRep vcenterDataCenterRestRep = api(context).vcenterDataCenters().get(vcenterDatacenter.getId());
return Arrays.asList(vcenterDataCenterRestRep);
}
}
protected List<HostRestRep> listEsxHostsByDatacenter(AssetOptionsContext context, URI datacenterId) {
return api(context).hosts().getByDataCenter(datacenterId, HostTypeFilter.ESX.and(REGISTERED).and(INCOMPATIBLE.not()).and(COMPLETE));
}
protected List<String> listFileDatastoresByProjectAndDatacenter(AssetOptionsContext context, URI projectId,
URI datacenterId) {
return new VMwareDatastoreTagger(api(context)).getDatastoreNames(projectId, datacenterId);
}
@Asset("vcenter")
public List<AssetOption> getVCenters(AssetOptionsContext context) {
debug("getting vcenters");
return createBaseResourceOptions(listVCenters(context));
}
@Asset("datacenter")
@AssetDependencies("vcenter")
public List<AssetOption> getDatacenters(AssetOptionsContext context, URI vcenter) {
debug("getting datacenters (vcenter=%s)", vcenter);
return createBaseResourceOptions(listDatacentersByVCenter(context, vcenter));
}
@Asset("esxHost")
@AssetDependencies({ "datacenter" })
public List<AssetOption> getEsxHosts(AssetOptionsContext context, URI datacenter) {
debug("getting esxHosts (datacenter=%s)", datacenter);
return createHostOptions(context, listEsxHostsByDatacenter(context, datacenter));
}
@Asset("esxHost")
@AssetDependencies({ "datacenter", "blockStorageType" })
public List<AssetOption> getEsxHosts(AssetOptionsContext context, URI datacenter, String storageType) {
debug("getting esxHosts (datacenter=%s)", datacenter);
List<HostRestRep> esxHosts = listEsxHostsByDatacenter(context, datacenter);
filterClusterHosts(context, datacenter, storageType, esxHosts);
return getHostOrClusterOptions(context, esxHosts, storageType);
}
/**
* Filter out hosts in the cluster when other hosts in that cluster have failed discovery or
* are incompatible.
*
* @param context
* asset context
* @param datacenter
* data center
* @param storageType
* storage type (exclusive or shared)
* @param esxHosts
* list of all esx hosts that are discovered and compatible
*/
private void filterClusterHosts(AssetOptionsContext context, URI datacenter, String storageType, List<HostRestRep> esxHosts) {
// Gather all ESX hosts that didn't match the filter
if (esxHosts != null && !esxHosts.isEmpty() && storageType.equalsIgnoreCase(BlockProvider.SHARED_STORAGE.toString())) {
List<HostRestRep> misfitEsxHosts = api(context).hosts().getByDataCenter(datacenter,
HostTypeFilter.ESX.and(REGISTERED.not()).or(INCOMPATIBLE).or(COMPLETE.not()));
Set<URI> misfitEsxClusterIds = new HashSet<>();
if (misfitEsxHosts != null && !misfitEsxHosts.isEmpty()) {
for (HostRestRep misfitEsxHost : misfitEsxHosts) {
if (misfitEsxHost.getCluster() != null && !NullColumnValueGetter.isNullURI(misfitEsxHost.getCluster().getId())) {
misfitEsxClusterIds.add(misfitEsxHost.getCluster().getId());
}
}
Iterator<HostRestRep> esxHostIter = esxHosts.iterator();
while (esxHostIter.hasNext()) {
HostRestRep esxHost = esxHostIter.next();
if (esxHost.getCluster() != null && !NullColumnValueGetter.isNullURI(esxHost.getCluster().getId())
&& misfitEsxClusterIds.contains(esxHost.getCluster().getId())) {
esxHostIter.remove();
}
}
}
}
}
@Asset("datastore")
@AssetDependencies({ "datacenter", "project" })
public List<AssetOption> getFileDatastores(AssetOptionsContext context, URI datacenter, URI project) {
debug("getting fileDatastores (datacenter=%s, project=%s)", datacenter, project);
return createStringOptions(listFileDatastoresByProjectAndDatacenter(context, project, datacenter));
}
private static List<URI> getVolumeList(List<? extends BlockObjectRestRep> objects) {
List<URI> volumes = Lists.newArrayList();
for (BlockObjectRestRep rep : objects) {
volumes.add(rep.getId());
}
return volumes;
}
@Asset("unassignedBlockDatastore")
@AssetDependencies({ "esxHost", "project" })
public List<AssetOption> getUnassignedDatastores(AssetOptionsContext ctx, URI hostOrClusterId, final URI projectId) {
ViPRCoreClient client = api(ctx);
Set<URI> exportedBlockResources = BlockProvider.getExportedVolumes(api(ctx), projectId, hostOrClusterId, null);
UnexportedBlockResourceFilter<VolumeRestRep> unexportedFilter = new UnexportedBlockResourceFilter<VolumeRestRep>(
exportedBlockResources);
SourceTargetVolumesFilter sourceTargetVolumesFilter = new SourceTargetVolumesFilter();
BlockVolumeBootVolumeFilter bootVolumeFilter = new BlockVolumeBootVolumeFilter();
List<VolumeRestRep> volumes = client.blockVolumes().findByProject(projectId, unexportedFilter.and(sourceTargetVolumesFilter).and(bootVolumeFilter.not()));
return createBlockVolumeDatastoreOptions(volumes, hostOrClusterId);
}
@Asset("assignedBlockDatastore")
@AssetDependencies("esxHost")
public List<AssetOption> getAssignedDatastores(AssetOptionsContext context, URI esxHost) {
debug("getting blockDatastores (esxHost=%s)", esxHost);
List<ExportGroupRestRep> exports = BlockProviderUtils.getExportsForHostOrCluster(api(context), context.getTenant(), esxHost);
Collection<ExportGroupRestRep> filteredExportGroups = BlockStorageUtils.filterExportsByType(exports, esxHost);
Set<URI> volumeIds = BlockProviderUtils.getExportedResourceIds(filteredExportGroups, ResourceType.VOLUME);
Set<URI> snapshotIds = BlockProviderUtils.getExportedResourceIds(filteredExportGroups, ResourceType.BLOCK_SNAPSHOT);
List<BlockObjectRestRep> resources = new ArrayList<>();
resources.addAll(api(context).blockVolumes().getByIds(volumeIds));
resources.addAll(api(context).blockSnapshots().getByIds(snapshotIds));
return createBlockVolumeDatastoreOptions(resources, esxHost);
}
@Asset("blockdatastore")
@AssetDependencies("esxHost")
public List<AssetOption> getBlockDatastores(AssetOptionsContext context, URI esxHost) {
debug("getting blockDatastores (esxHost=%s)", esxHost);
List<? extends BlockObjectRestRep> mountedVolumes = BlockProviderUtils.getBlockResources(
api(context), context.getTenant(), esxHost, true);
return createDatastoreOptions(mountedVolumes, esxHost);
}
@Asset("vcentersForEsxCluster")
@AssetDependencies("esxCluster")
public List<AssetOption> getVcentersForEsxCluster(AssetOptionsContext context, URI esxCluster) {
debug("getting vcenters for esxCluster");
return createBaseResourceOptions(listVcentersForCluster(context, esxCluster));
}
@Asset("datacentersForEsxCluster")
@AssetDependencies({ "vcentersForEsxCluster", "esxCluster" })
public List<AssetOption> getDatacentersForEsxCluster(AssetOptionsContext context, URI vcentersForEsxCluster, URI esxCluster) {
debug("getting datacenters (vcenter=%s, cluster=%s)", vcentersForEsxCluster, esxCluster);
return createBaseResourceOptions(listDatacentersByVCenterAndCluster(context, vcentersForEsxCluster, esxCluster));
}
protected static List<AssetOption> createBlockVolumeDatastoreOptions(List<? extends BlockObjectRestRep> mountedVolumes, URI hostId) {
List<AssetOption> options = Lists.newArrayList();
for (BlockObjectRestRep volume : mountedVolumes) {
Set<String> datastoreNames = VMwareDatastoreTagger.getDatastoreNames(volume);
String datastoresLabel = datastoreNames.isEmpty() ? "N/A" : StringUtils.join(datastoreNames, ",");
options.add(newAssetOption(volume.getId(), "volume.hlu.datastore", volume.getName(), datastoresLabel, volume.getWwn()));
}
AssetOptionsUtils.sortOptionsByLabel(options);
return options;
}
private Map<URI, Integer> getVolumeHLUs(AssetOptionsContext context, List<URI> volumeIds) {
BulkIdParam bulkParam = new BulkIdParam();
Map<URI, Integer> volumeHLUs = Maps.newHashMap();
bulkParam.getIds().addAll(volumeIds);
List<ITLRestRep> bulkResponse = api(context).blockVolumes().getExports(bulkParam).getExportList();
for (ITLRestRep export : bulkResponse) {
volumeHLUs.put(export.getBlockObject().getId(), export.getHlu());
}
return volumeHLUs;
}
protected static List<AssetOption> createDatastoreOptions(List<? extends BlockObjectRestRep> mountedVolumes, URI hostId) {
Map<String, List<String>> datastores = Maps.newHashMap(); // There can be multiple volumes to a DS, so de-dupe in a hash map
for (BlockObjectRestRep volume : mountedVolumes) {
String key = KnownMachineTags.getBlockVolumeVMFSDatastore(hostId, volume);
List<String> values = Lists.newArrayList();
if (datastores.containsKey(key)) {
values = datastores.get(key);
}
values.add(volume.getWwn());
datastores.put(key, values);
}
List<AssetOption> options = Lists.newArrayList();
for (Map.Entry<String, List<String>> datastore : datastores.entrySet()) {
options.add(new AssetOption(datastore.getKey(), getMessage("datastore.label", datastore.getKey(), String.join(",", datastore.getValue()))));
}
AssetOptionsUtils.sortOptionsByLabel(options);
return options;
}
}