2121
2222
2323import com .cloud .agent .api .StoragePoolInfo ;
24+ import com .cloud .dc .ClusterVO ;
25+ import com .cloud .dc .dao .ClusterDao ;
26+ import com .cloud .host .HostVO ;
2427import com .cloud .hypervisor .Hypervisor ;
28+ import com .cloud .resource .ResourceManager ;
29+ import com .cloud .storage .Storage ;
30+ import com .cloud .storage .StorageManager ;
2531import com .cloud .storage .StoragePool ;
32+ import com .cloud .utils .exception .CloudRuntimeException ;
33+ import com .google .common .base .Preconditions ;
2634import org .apache .cloudstack .engine .subsystem .api .storage .ClusterScope ;
2735import org .apache .cloudstack .engine .subsystem .api .storage .DataStore ;
2836import org .apache .cloudstack .engine .subsystem .api .storage .HostScope ;
37+ import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStoreInfo ;
2938import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStoreLifeCycle ;
39+ import org .apache .cloudstack .engine .subsystem .api .storage .PrimaryDataStoreParameters ;
3040import org .apache .cloudstack .engine .subsystem .api .storage .ZoneScope ;
41+ import org .apache .cloudstack .storage .datastore .lifecycle .BasePrimaryDataStoreLifeCycleImpl ;
42+ import org .apache .cloudstack .storage .feign .model .OntapStorage ;
43+ import org .apache .cloudstack .storage .provider .StorageProviderFactory ;
44+ import org .apache .cloudstack .storage .service .StorageStrategy ;
45+ import org .apache .cloudstack .storage .utils .Constants ;
46+ import org .apache .cloudstack .storage .utils .Constants .ProtocolType ;
47+ import org .apache .cloudstack .storage .volume .datastore .PrimaryDataStoreHelper ;
3148import org .apache .logging .log4j .LogManager ;
3249import org .apache .logging .log4j .Logger ;
33- import java .util .Map ;
3450
35- public class OntapPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle {
51+ import javax .inject .Inject ;
52+ import java .util .List ;
53+ import java .util .Map ;
54+ import java .util .UUID ;
3655
56+ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle {
57+ @ Inject private ClusterDao _clusterDao ;
58+ @ Inject private StorageManager _storageMgr ;
59+ @ Inject private ResourceManager _resourceMgr ;
60+ @ Inject private PrimaryDataStoreHelper _dataStoreHelper ;
3761 private static final Logger s_logger = (Logger )LogManager .getLogger (OntapPrimaryDatastoreLifecycle .class );
3862
3963 /**
@@ -43,14 +67,120 @@ public class OntapPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle
4367 */
4468 @ Override
4569 public DataStore initialize (Map <String , Object > dsInfos ) {
46-
47- return null ;
48-
70+ if (dsInfos == null ) {
71+ throw new CloudRuntimeException ("Datastore info map is null, cannot create primary storage" );
72+ }
73+ String url = dsInfos .get ("url" ).toString (); // TODO: Decide on whether should the customer enter just the Management LIF IP or https://ManagementLIF
74+ Long zoneId = dsInfos .get ("zoneId" ).toString ().trim ().isEmpty () ? null : (Long )dsInfos .get ("zoneId" );
75+ Long podId = dsInfos .get ("podId" ).toString ().trim ().isEmpty () ? null : (Long )dsInfos .get ("zoneId" );
76+ Long clusterId = dsInfos .get ("clusterId" ).toString ().trim ().isEmpty () ? null : (Long )dsInfos .get ("clusterId" );
77+ String storagePoolName = dsInfos .get ("name" ).toString ().trim ();
78+ String providerName = dsInfos .get ("providerName" ).toString ().trim ();
79+ String tags = dsInfos .get ("tags" ).toString ().trim ();
80+ Boolean isTagARule = (Boolean ) dsInfos .get ("isTagARule" );
81+ String scheme = dsInfos .get ("scheme" ).toString ();
82+
83+ s_logger .info ("Creating ONTAP primary storage pool with name: " + storagePoolName + ", provider: " + providerName +
84+ ", zoneId: " + zoneId + ", podId: " + podId + ", clusterId: " + clusterId + ", protocol: " + scheme );
85+
86+ // Additional details requested for ONTAP primary storage pool creation
87+ @ SuppressWarnings ("unchecked" )
88+ Map <String , String > details = (Map <String , String >)dsInfos .get ("details" );
89+ // Validations
90+ if (podId == null ^ clusterId == null ) {
91+ throw new CloudRuntimeException ("Cluster Id or Pod Id is null, cannot create primary storage" );
92+ }
93+
94+ if (podId == null && clusterId == null ) {
95+ if (zoneId != null ) {
96+ s_logger .info ("Both Pod Id and Cluster Id are null, Primary storage pool will be associated with a Zone" );
97+ } else {
98+ throw new CloudRuntimeException ("Pod Id, Cluster Id and Zone Id are all null, cannot create primary storage" );
99+ }
100+ }
101+
102+ if (storagePoolName == null || storagePoolName .isEmpty ()) {
103+ throw new CloudRuntimeException ("Storage pool name is null or empty, cannot create primary storage" );
104+ }
105+
106+ if (providerName == null || providerName .isEmpty ()) {
107+ throw new CloudRuntimeException ("Provider name is null or empty, cannot create primary storage" );
108+ }
109+
110+ PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters ();
111+ if (clusterId != null ) {
112+ ClusterVO clusterVO = _clusterDao .findById (clusterId );
113+ Preconditions .checkNotNull (clusterVO , "Unable to locate the specified cluster" );
114+ if (clusterVO .getHypervisorType () != Hypervisor .HypervisorType .KVM ) {
115+ throw new CloudRuntimeException ("ONTAP primary storage is supported only for KVM hypervisor" );
116+ }
117+ parameters .setHypervisorType (clusterVO .getHypervisorType ());
118+ }
119+
120+ // TODO: While testing need to check what does this actually do and if the fields corresponding to each protocol should also be set
121+ // TODO: scheme could be 'custom' in our case and we might have to ask 'protocol' separately to the user
122+ ProtocolType protocol = ProtocolType .valueOf (details .get (Constants .PROTOCOL ).toLowerCase ());
123+ switch (protocol ) {
124+ case NFS :
125+ parameters .setType (Storage .StoragePoolType .NetworkFilesystem );
126+ break ;
127+ case ISCSI :
128+ parameters .setType (Storage .StoragePoolType .Iscsi );
129+ break ;
130+ default :
131+ throw new CloudRuntimeException ("Unsupported protocol: " + scheme + ", cannot create primary storage" );
132+ }
133+
134+ details .put (Constants .MANAGEMENT_LIF , url );
135+
136+ // Validate the ONTAP details
137+ if (details .get (Constants .IS_DISAGGREGATED ) == null || details .get (Constants .IS_DISAGGREGATED ).isEmpty ()) {
138+ details .put (Constants .IS_DISAGGREGATED , "false" );
139+ }
140+
141+ OntapStorage ontapStorage = new OntapStorage (details .get (Constants .USERNAME ), details .get (Constants .PASSWORD ),
142+ details .get (Constants .MANAGEMENT_LIF ), details .get (Constants .SVM_NAME ), protocol ,
143+ Boolean .parseBoolean (details .get (Constants .IS_DISAGGREGATED )));
144+ StorageStrategy storageStrategy = StorageProviderFactory .getStrategy (ontapStorage );
145+ boolean isValid = storageStrategy .connect ();
146+ if (isValid ) {
147+ // String volumeName = storagePoolName + "_vol"; //TODO: Figure out a better naming convention
148+ storageStrategy .createVolume (storagePoolName , Long .parseLong ((details .get ("size" )))); // TODO: size should be in bytes, so see if conversion is needed
149+ } else {
150+ throw new CloudRuntimeException ("ONTAP details validation failed, cannot create primary storage" );
151+ }
152+
153+ parameters .setTags (tags );
154+ parameters .setIsTagARule (isTagARule );
155+ parameters .setDetails (details );
156+ parameters .setUuid (UUID .randomUUID ().toString ());
157+ parameters .setZoneId (zoneId );
158+ parameters .setPodId (podId );
159+ parameters .setClusterId (clusterId );
160+ parameters .setName (storagePoolName );
161+ parameters .setProviderName (providerName );
162+ parameters .setManaged (true );
163+
164+ return _dataStoreHelper .createPrimaryDataStore (parameters );
49165 }
50166
51167 @ Override
52- public boolean attachCluster (DataStore store , ClusterScope scope ) {
53- return false ;
168+ public boolean attachCluster (DataStore dataStore , ClusterScope scope ) {
169+ logger .debug ("In attachCluster for ONTAP primary storage" );
170+ PrimaryDataStoreInfo primarystore = (PrimaryDataStoreInfo )dataStore ;
171+ List <HostVO > hostsToConnect = _resourceMgr .getEligibleUpAndEnabledHostsInClusterForStorageConnection (primarystore );
172+
173+ logger .debug (String .format ("Attaching the pool to each of the hosts %s in the cluster: %s" , hostsToConnect , primarystore .getClusterId ()));
174+ for (HostVO host : hostsToConnect ) {
175+ // TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
176+ try {
177+ _storageMgr .connectHostToSharedPool (host , dataStore .getId ());
178+ } catch (Exception e ) {
179+ logger .warn ("Unable to establish a connection between " + host + " and " + dataStore , e );
180+ }
181+ }
182+ _dataStoreHelper .attachCluster (dataStore );
183+ return true ;
54184 }
55185
56186 @ Override
@@ -60,7 +190,20 @@ public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo exis
60190
61191 @ Override
62192 public boolean attachZone (DataStore dataStore , ZoneScope scope , Hypervisor .HypervisorType hypervisorType ) {
63- return false ;
193+ logger .debug ("In attachZone for ONTAP primary storage" );
194+ List <HostVO > hostsToConnect = _resourceMgr .getEligibleUpAndEnabledHostsInZoneForStorageConnection (dataStore , scope .getScopeId (), Hypervisor .HypervisorType .KVM );
195+
196+ logger .debug (String .format ("In createPool. Attaching the pool to each of the hosts in %s." , hostsToConnect ));
197+ for (HostVO host : hostsToConnect ) {
198+ // TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
199+ try {
200+ _storageMgr .connectHostToSharedPool (host , dataStore .getId ());
201+ } catch (Exception e ) {
202+ logger .warn ("Unable to establish a connection between " + host + " and " + dataStore , e );
203+ }
204+ }
205+ _dataStoreHelper .attachZone (dataStore );
206+ return true ;
64207 }
65208
66209 @ Override
0 commit comments