/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.customconfigcontroller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.emc.storageos.db.client.DbModelClient;
import com.emc.storageos.db.client.model.Cluster;
import com.emc.storageos.db.client.model.DataObject;
import com.emc.storageos.db.client.model.Host;
import com.emc.storageos.db.client.model.Initiator;
import com.emc.storageos.db.client.model.Network;
import com.emc.storageos.db.client.model.Project;
import com.emc.storageos.db.client.model.StoragePort;
import com.emc.storageos.db.client.model.StorageSystem;
import com.emc.storageos.db.client.model.TenantOrg;
import com.emc.storageos.db.client.model.VirtualPool;
import com.emc.storageos.db.client.model.Volume;
import com.emc.storageos.db.client.util.DataObjectUtils;
import com.google.common.base.Strings;
/**
* This factory creates instances of {@link DataSource} from a list
* of {@link DataObject} or properties. This is a utility class.
*
*/
public class DataSourceFactory {
private CustomConfigTypeProvider configTypeProvider;
private DbModelClient dbModelClient;
/**
* An instance of DbModelClient to handle db queries
*
* @return instance of DbModelClient to handle db queries
*/
public DbModelClient getDbModelClient() {
return dbModelClient;
}
/**
* An instance of DbModelClient to handle db queries
*
* @param dbModelClient
*/
public void setDbModelClient(DbModelClient dbModelClient) {
this.dbModelClient = dbModelClient;
}
/**
* Returns an instance of the provider that holds the configType definitions
*
* @return an instance of the provider that holds the configType definitions
*/
public CustomConfigTypeProvider getConfigTypeProvider() {
return configTypeProvider;
}
public void setConfigTypeProvider(CustomConfigTypeProvider configTypeProvider) {
this.configTypeProvider = configTypeProvider;
}
/**
* Creates a data source from a collection of model objects for a given
* customizable configuration.
*
* @param configName the name of the customizable configuration
* @param objs a collection of objects. It is expected that all objects
* required to fully populate the source with all the properties
* needed is provided. The factory does not ensure that the data
* source is fully populated.
* @return a data source populated with the properties needed to resolve
* a name mask. It is the responsibility of the caller
* to ensure all needed objects to resolve a name mask are supplied.
*/
public DataSource createDataSource(String configName, DataObject[] objs) {
CustomConfigType item = configTypeProvider
.getCustomConfigType(configName);
Map<Class<? extends DataObject>, DataObject> objectsMap = toMap(objs);
DataSource dataSource = new DataSource();
DataObject object = null;
Object val = null;
for (DataSourceVariable prop : item.getDataSourceVariables().keySet()) {
object = objectsMap.get(prop.getSourceClass());
if (object != null) {
val = DataObjectUtils.getPropertyValue(object.getClass(),
object, prop.getPropertyName());
dataSource.addProperty(prop.getDisplayName(),
val == null ? "" : val.toString());
}
}
return dataSource;
}
/**
* Creates a data source from a collection of model objects and map of
* calculated values for a given customizable configuration.
*
* @param configName the name of the customizable configuration
* @param objs a collection of objects. It is expected that all objects
* required to fully populate the source with all the properties
* needed is provided. The factory does not ensure that the data
* source is fully populated.
* @param computedValueMap a map of values which is calculated and not directly
* retrievable from the objects.
* @return a data source populated with the properties needed to resolve
* a name mask. It is the responsibility of the caller
* to ensure all needed objects to resolve a name mask are supplied.
*/
public DataSource createDataSource(String configName, DataObject[] objs, Map<String, String> computedValueMap) {
CustomConfigType item = configTypeProvider
.getCustomConfigType(configName);
Map<Class<? extends DataObject>, DataObject> objectsMap = toMap(objs);
DataSource dataSource = new DataSource();
DataObject object = null;
Object val = null;
for (DataSourceVariable prop : item.getDataSourceVariables().keySet()) {
if (prop instanceof ComputedDataSourceVariable) {
val = computedValueMap.get(((ComputedDataSourceVariable) prop).getComputedPropertyName());
} else {
object = objectsMap.get(prop.getSourceClass());
if (object != null) {
val = DataObjectUtils.getPropertyValue(object.getClass(),
object, prop.getPropertyName());
}
}
dataSource.addProperty(prop.getDisplayName(),
val == null ? "" : val.toString());
}
return dataSource;
}
/**
* Creates a data source for resolving a zone name using the provided
* parameters. This is a utility function created for the purpose of
* reducing clutter in the export code.
*
* @param host the instance of the host for which the zone will be created
* @param initiator the instance of the initiator for which the zone will be created
* @param port the instance of the port for which the zone will be created
* @param network the instance of the network where the zone will be created
* @param storageSystem the instance of the storage system of the zone's port
* @return a data source populated with the properties needed to resolve
* a zone name
*/
public DataSource createZoneNameDataSource(Host host, Initiator initiator,
StoragePort port, Network network, StorageSystem storageSystem) {
DataSource source = createDataSource(CustomConfigConstants.ZONE_MASK_NAME,
new DataObject[] { host, initiator, port, network, storageSystem });
return source;
}
/**
* Creates a data source for resolving a zone name using the provided
* parameters. This is a utility function created for the purpose of
* reducing clutter in the export code.
*
* @param hostName the name of the host for which the zone will be created
* @param initiator the initiator for which the zone will be created
* @param port the instance of the port for which the zone will be created
* @param storageSystem the instance of the storage system of the zone's port
* @param nativeId the nativeId of the network where the zone will be created
* @return a data source populated with the properties needed to resolve
* a zone name
*/
public DataSource createZoneNameDataSource(String hostName, Initiator initiator,
StoragePort port, String nativeId, StorageSystem storageSystem) {
// we want to avoid looking up the network because of all the endpoints
Network network = new Network();
network.setNativeId(nativeId);
return createZoneNameDataSource(getHostByName(hostName), initiator, port, network, storageSystem);
}
/**
* Creates a datasource used for export operations
*
* @param configName - name of the config for which the export datasource should be created
* @param host - host instance
* @param cluster - cluster instance
* @param storageSystem - storage system instance
* @return a data source populated with the properties needed
*/
public DataSource createExportMaskDataSource(String configName, Host host, Cluster cluster, StorageSystem storageSystem) {
return createDataSource(configName, new DataObject[] { host, cluster, storageSystem });
}
/**
* Creates a datasource used for export operations
*
* @param configName - name of the config for which the export datasource should be created
* @param hostName - host name
* @param clusterName - cluster name
* @param storageSystem - storage system instance
* @return a data source populated with the properties needed
*/
public DataSource createExportMaskDataSource(String configName, String hostName, String clusterName, StorageSystem storageSystem) {
// for host, get the object because the name may query for host type
Host host = getHostByName(hostName);
// for cluster, just create an in-memory object, cluster has no other attributes
Cluster cluster = new Cluster();
if (!Strings.isNullOrEmpty(clusterName)) {
cluster.setLabel(clusterName);
}
return createExportMaskDataSource(configName, host, cluster, storageSystem);
}
/**
* Creates a datasource for resolving HSD's nickname
*
* @param hostName - host name
* @param storagePortNumber - HDS storage port number
* @return a data source populated with the properties needed
*/
public DataSource createHSDNickNameDataSource(String hostName, String storagePortNumber, StorageSystem storageSystem) {
Host host = getHostByName(hostName);
Map<String, String> computedValueMap = new HashMap<String, String>();
computedValueMap.put(CustomConfigConstants.HDS_STORAGE_PORT_NUMBER, storagePortNumber);
return createDataSource(CustomConfigConstants.HDS_HOST_STORAGE_DOMAIN_NICKNAME_MASK_NAME, new DataObject[] { host, storageSystem },
computedValueMap);
}
/**
* Create a data source object for resolving XtremIO Volume folder names
*
* @param project the project that the volume will be created on
* @param storageSystem the storageSystem that the volume will be created on.
* @return a data source populated with the properties
*/
public DataSource createXtremIOVolumeFolderNameDataSource(Project project, StorageSystem storageSystem) {
return createDataSource(CustomConfigConstants.XTREMIO_VOLUME_FOLDER_NAME,
new DataObject[] { project, storageSystem });
}
/**
* Create a data source object for resolving XtremIO initiator group names
*
* @param Host the Host that the initiators belong to
* @param storageSystem the storageSystem that the volume will be created on.
* @return a data source populated with the properties
*/
public DataSource createXtremIOInitiatorGroupNameDataSource(String hostName, StorageSystem storageSystem) {
Host host = getHostByName(hostName);
return createDataSource(CustomConfigConstants.XTREMIO_INITIATOR_GROUP_NAME,
new DataObject[] { host, storageSystem });
}
/**
* Create a data source object for resolving XtremIO initiator group folder names
*
* @param host the host that the initiator group folder belong to
* @param storageSystem the storageSystem that the volume will be created on.
* @return a data source populated with the properties
*/
public DataSource createXtremIOHostInitiatorGroupFolderNameDataSource(String hostName, StorageSystem storageSystem) {
Host host = getHostByName(hostName);
return createDataSource(CustomConfigConstants.XTREMIO_HOST_INITIATOR_GROUP_FOLDER_NAME,
new DataObject[] { host, storageSystem });
}
/**
* Create a data source object for resolving XtremIO initiator group folder names
*
* @param cluster the cluster that the initiator group folder belong to
* @param storageSystem the storageSystem that the volume will be created on.
* @return a data source populated with the properties
*/
public DataSource createXtremIOClusterInitiatorGroupFolderNameDataSource(String clusterName, StorageSystem storageSystem) {
Cluster cluster = new Cluster();
if (!Strings.isNullOrEmpty(clusterName)) {
cluster.setLabel(clusterName);
}
return createDataSource(CustomConfigConstants.XTREMIO_CLUSTER_INITIATOR_GROUP_FOLDER_NAME,
new DataObject[] { cluster, storageSystem });
}
/**
* Creates a datasource that can be used to populate the custom name for a volume.
*
* @param project A reference to the volume's project.
* @param tenant A reference to the passed project's tenant.
* @param volumeLabel The user supplied volume label.
* @param volumeWWN The volume WWN.
* @param exportName The host or cluster name or null.
* @param configName The name of the custom configuration.
*
* @return A reference to the created DataSource instance.
*/
public DataSource createCustomVolumeNameDataSource(Project project, TenantOrg tenant, String volumeLabel,
String volumeWWN, String exportName, String configName) {
// Create a list of the DataObjects for the datasource to be created
// and add the passed project and tenant to the list.
List<DataObject> dataObjectsList = new ArrayList<>();
dataObjectsList.add(project);
dataObjectsList.add(tenant);
// Create a Volume instance and populate those components of the datasource
// that come from a Volume. Add the volume to the DataObjects list.
Volume volume = new Volume();
volume.setLabel(volumeLabel);
volume.setWWN(volumeWWN);
dataObjectsList.add(volume);
// Add the host or cluster name if specified as a computed value since
// we do not know if this is a host or cluster export.
Map<String, String> computedValueMap = new HashMap<String, String>();
if (exportName != null) {
computedValueMap.put(CustomConfigConstants.CUSTOM_VOLUME_EXPORT_NAME, exportName);
}
// Create the datasource.
DataObject[] dataObjects = new DataObject[dataObjectsList.size()];
return createDataSource(configName, dataObjectsList.toArray(dataObjects), computedValueMap);
}
/**
* Create a data source object for resolving Isilon file system path
*
* @param project the project that the file system will be created under.
* @param vPool the virtual pool on file system will be created on.
* @param tenant the tenant by which file system is created by.
* @param storageSystem the storageSystem that the file system will be created on.
* @return a data source populated with the properties
*/
public DataSource createIsilonFileSystemPathDataSource(Project project, VirtualPool vPool, TenantOrg tenant,
StorageSystem storageSystem) {
return createDataSource(CustomConfigConstants.ISILON_PATH_CUSTOMIZATION,
new DataObject[] { project, vPool, tenant, storageSystem });
}
/**
* Create a data source object for resolving Isilon unmanaged file system locations
*
* @param storageSystem the storageSystem that the file system will be created on.
* @return a data source populated with the properties
*/
public DataSource createIsilonUnmanagedFileSystemLocationsDataSource(StorageSystem storageSystem) {
return createDataSource(CustomConfigConstants.ISILON_UNMANAGED_FILE_SYSTEM_LOCATIONS,
new DataObject[] { storageSystem });
}
/**
* Creates a data source objects for a customizable configuration
* using the sample data stored in the configType definition.
*
* @param configName the name of the customizable configuration
* @return a data source objects populated with sample data for the properties
* required to resolve a name mask for a customizable configuration.
*/
public DataSource getSampleDataSource(String configName) {
CustomConfigType item = configTypeProvider
.getCustomConfigType(configName);
DataSource dataSource = new DataSource();
for (DataSourceVariable prop : item.getDataSourceVariables().keySet()) {
dataSource.addProperty(prop.getDisplayName(), prop.getSample());
}
return dataSource;
}
/**
* A utility function to create a map of class-to-object from a
* list of objects.
*
* @param objects the list of objects.
* @return a map of class-to-object
*/
private Map<Class<? extends DataObject>, DataObject> toMap(DataObject[] objects) {
Map<Class<? extends DataObject>, DataObject> objectsMap = new HashMap<Class<? extends DataObject>, DataObject>();
for (DataObject object : objects) {
if (object != null) {
objectsMap.put(object.getClass(), object);
}
}
return objectsMap;
}
private Host getHostByName(String hostName) {
// for host, get the object because the name may query for host type
Host host = null;
if (hostName != null && !hostName.isEmpty()) {
host = dbModelClient.findByUniqueAlternateId(Host.class,
Host.ALTER_ID_FIELD, hostName);
if (host == null) {
host = new Host();
host.setHostName(hostName);
host.setLabel(hostName);
// Internal hosts such as Vplex cannot be discovered,
// so Other is the correct type
host.setType(Host.HostType.Other.name());
}
}
return host;
}
}