@@ -61,10 +61,13 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
6161 @ Inject private PrimaryDataStoreHelper _dataStoreHelper ;
6262 private static final Logger s_logger = LogManager .getLogger (OntapPrimaryDatastoreLifecycle .class );
6363
64+ // ONTAP minimum volume size is 1.56 GB (1677721600 bytes)
65+ private static final long ONTAP_MIN_VOLUME_SIZE = 1677721600L ;
66+
6467 /**
6568 * Creates primary storage on NetApp storage
66- * @param dsInfos
67- * @return
69+ * @param dsInfos datastore information map
70+ * @return DataStore instance
6871 */
6972 @ Override
7073 public DataStore initialize (Map <String , Object > dsInfos ) {
@@ -80,20 +83,25 @@ public DataStore initialize(Map<String, Object> dsInfos) {
8083 Long capacityBytes = (Long ) dsInfos .get ("capacityBytes" );
8184 String tags = (String ) dsInfos .get ("tags" );
8285 Boolean isTagARule = (Boolean ) dsInfos .get ("isTagARule" );
83- String scheme = dsInfos .get ("scheme" ).toString ();
8486
8587 s_logger .info ("Creating ONTAP primary storage pool with name: " + storagePoolName + ", provider: " + providerName +
86- ", zoneId: " + zoneId + ", podId: " + podId + ", clusterId: " + clusterId + ", scheme: " + scheme );
88+ ", zoneId: " + zoneId + ", podId: " + podId + ", clusterId: " + clusterId );
89+ s_logger .debug ("Received capacityBytes from UI: " + capacityBytes );
8790
8891 // Additional details requested for ONTAP primary storage pool creation
8992 @ SuppressWarnings ("unchecked" )
9093 Map <String , String > details = (Map <String , String >) dsInfos .get ("details" );
91- // Validations
92- if (capacityBytes == null || capacityBytes <= 1677721600L ) { // 1.56 GB minimum for ONTAP Volume
93- throw new IllegalArgumentException ("'capacityBytes' must be present and greater than 0." );
94+
95+ // Validate and set capacity
96+ if (capacityBytes == null || capacityBytes <= 0 ) {
97+ s_logger .warn ("capacityBytes not provided or invalid (" + capacityBytes + "), using ONTAP minimum size: " + ONTAP_MIN_VOLUME_SIZE );
98+ capacityBytes = ONTAP_MIN_VOLUME_SIZE ;
99+ } else if (capacityBytes < ONTAP_MIN_VOLUME_SIZE ) {
100+ s_logger .warn ("capacityBytes (" + capacityBytes + ") is below ONTAP minimum (" + ONTAP_MIN_VOLUME_SIZE + "), adjusting to minimum" );
101+ capacityBytes = ONTAP_MIN_VOLUME_SIZE ;
94102 }
95- details .put (Constants .SIZE , capacityBytes .toString ());
96103
104+ // Validate scope
97105 if (podId == null ^ clusterId == null ) {
98106 throw new CloudRuntimeException ("Cluster Id or Pod Id is null, cannot create primary storage" );
99107 }
@@ -124,7 +132,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
124132 parameters .setHypervisorType (clusterVO .getHypervisorType ());
125133 }
126134
127- // Get ONTAP details from the URL
135+ // Required ONTAP detail keys
128136 Set <String > requiredKeys = Set .of (
129137 Constants .USERNAME ,
130138 Constants .PASSWORD ,
@@ -135,80 +143,95 @@ public DataStore initialize(Map<String, Object> dsInfos) {
135143 );
136144
137145 // Parse key=value pairs from URL into details (skip empty segments)
138- for (String segment : url .split (Constants .SEMICOLON )) {
139- if (segment .isEmpty ()) {
140- continue ;
141- }
142- String [] kv = segment .split (Constants .EQUALS , 2 );
143- if (kv .length == 2 ) {
144- details .put (kv [0 ].trim (), kv [1 ].trim ());
146+ if (url != null && !url .isEmpty ()) {
147+ for (String segment : url .split (";" )) {
148+ if (segment .isEmpty ()) {
149+ continue ;
150+ }
151+ String [] kv = segment .split ("=" , 2 );
152+ if (kv .length == 2 ) {
153+ details .put (kv [0 ].trim (), kv [1 ].trim ());
154+ }
145155 }
146156 }
147157
148- // Validate existing entries (unexpected keys, empty values)
158+ // Validate existing entries (reject unexpected keys, empty values)
149159 for (Map .Entry <String , String > e : details .entrySet ()) {
150160 String key = e .getKey ();
151161 String val = e .getValue ();
152162 if (!requiredKeys .contains (key )) {
153- throw new CloudRuntimeException ("Unexpected ONTAP key passed in url : " + key );
163+ throw new CloudRuntimeException ("Unexpected ONTAP detail key in URL : " + key );
154164 }
155165 if (val == null || val .isEmpty ()) {
156166 throw new CloudRuntimeException ("ONTAP primary storage creation failed, empty detail: " + key );
157167 }
158168 }
169+
159170 // Detect missing required keys
160- if (details .size () != requiredKeys .size ()) {
171+ Set <String > providedKeys = new java .util .HashSet <>(details .keySet ());
172+ if (!providedKeys .containsAll (requiredKeys )) {
161173 Set <String > missing = new java .util .HashSet <>(requiredKeys );
162- missing .removeAll (details .keySet ());
163- if (!missing .isEmpty ()) {
164- throw new CloudRuntimeException ("ONTAP primary storage creation failed, missing detail(s): " + missing );
165- }
174+ missing .removeAll (providedKeys );
175+ throw new CloudRuntimeException ("ONTAP primary storage creation failed, missing detail(s): " + missing );
166176 }
167- // Default for IS_DISAGGREGATED if needed (after validation of presence)
177+
178+ details .put (Constants .SIZE , capacityBytes .toString ());
179+
180+ // Default for IS_DISAGGREGATED if needed
168181 details .putIfAbsent (Constants .IS_DISAGGREGATED , "false" );
169182
170- // TODO: While testing need to check what does this actually do and if the fields corresponding to each protocol should also be set
171- // TODO: scheme could be 'custom' in our case and we might have to ask 'protocol' separately to the user
172- String path = "" ;
183+ // Determine storage pool type and path based on protocol
184+ String path ;
173185 ProtocolType protocol = ProtocolType .valueOf (details .get (Constants .PROTOCOL ));
174186 switch (protocol ) {
175187 case NFS3 :
176188 parameters .setType (Storage .StoragePoolType .NetworkFilesystem );
177- path = "/" + storagePoolName ;
189+ path = details .get (Constants .MANAGEMENT_LIF ) + ":/" + storagePoolName ;
190+ s_logger .info ("Setting NFS path for storage pool: " + path );
178191 break ;
179192 case ISCSI :
180193 parameters .setType (Storage .StoragePoolType .Iscsi );
181- //TODO: path for iSCSI
194+ path = "iqn.1992-08.com.netapp:" + details .get (Constants .SVM_NAME ) + "." + storagePoolName ;
195+ s_logger .info ("Setting iSCSI path for storage pool: " + path );
182196 break ;
183197 default :
184198 throw new CloudRuntimeException ("Unsupported protocol: " + protocol + ", cannot create primary storage" );
185199 }
186200
187- OntapStorage ontapStorage = new OntapStorage (details .get (Constants .USERNAME ), details .get (Constants .PASSWORD ),
188- details .get (Constants .MANAGEMENT_LIF ), details .get (Constants .SVM_NAME ), protocol ,
201+ // Connect to ONTAP and create volume
202+ OntapStorage ontapStorage = new OntapStorage (
203+ details .get (Constants .USERNAME ),
204+ details .get (Constants .PASSWORD ),
205+ details .get (Constants .MANAGEMENT_LIF ),
206+ details .get (Constants .SVM_NAME ),
207+ protocol ,
189208 Boolean .parseBoolean (details .get (Constants .IS_DISAGGREGATED ).toLowerCase ()));
209+
190210 StorageStrategy storageStrategy = StorageProviderFactory .getStrategy (ontapStorage );
191211 boolean isValid = storageStrategy .connect ();
192212 if (isValid ) {
193- // String volumeName = storagePoolName + "_vol"; //TODO: Figure out a better naming convention
194- storageStrategy .createStorageVolume (storagePoolName , Long .parseLong ((details .get (Constants .SIZE )))); // TODO: size should be in bytes, so see if conversion is needed
213+ long volumeSize = Long .parseLong (details .get (Constants .SIZE ));
214+ s_logger .info ("Creating ONTAP volume '" + storagePoolName + "' with size: " + volumeSize + " bytes (" +
215+ (volumeSize / (1024 * 1024 * 1024 )) + " GB)" );
216+ storageStrategy .createStorageVolume (storagePoolName , volumeSize );
195217 } else {
196218 throw new CloudRuntimeException ("ONTAP details validation failed, cannot create primary storage" );
197219 }
198220
221+ // Set parameters for primary data store
199222 parameters .setHost (details .get (Constants .MANAGEMENT_LIF ));
200223 parameters .setPort (Constants .ONTAP_PORT );
201224 parameters .setPath (path );
202- parameters .setTags (tags );
203- parameters .setIsTagARule (isTagARule );
225+ parameters .setTags (tags != null ? tags : "" );
226+ parameters .setIsTagARule (isTagARule != null ? isTagARule : Boolean . FALSE );
204227 parameters .setDetails (details );
205228 parameters .setUuid (UUID .randomUUID ().toString ());
206229 parameters .setZoneId (zoneId );
207230 parameters .setPodId (podId );
208231 parameters .setClusterId (clusterId );
209232 parameters .setName (storagePoolName );
210233 parameters .setProviderName (providerName );
211- parameters .setManaged (true );
234+ parameters .setManaged (true ); // ONTAP storage is always managed
212235 parameters .setCapacityBytes (capacityBytes );
213236 parameters .setUsedBytes (0 );
214237
@@ -302,3 +325,4 @@ public void changeStoragePoolScopeToCluster(DataStore store, ClusterScope cluste
302325
303326 }
304327}
328+
0 commit comments