From 67815a681304ab0480feb1580aa1bbf05e22594f Mon Sep 17 00:00:00 2001 From: "Jain, Rajiv" Date: Wed, 6 Aug 2025 19:39:18 +0530 Subject: [PATCH 1/4] Initial commit for Netapp primary and secondary storage plugin skeleton --- client/pom.xml | 10 + plugins/pom.xml | 2 + plugins/storage/image/netapp/pom.xml | 43 ++++ .../NetAppSecondaryDatastoreDriver.java | 91 +++++++++ .../NetAppSecondaryDatastoreLifecycle.java | 60 ++++++ .../NetAppSecondaryDatastoreProvider.java | 78 +++++++ .../storage-image-netapp/module.properties | 18 ++ .../spring-storage-image-netapp-context.xml | 33 +++ plugins/storage/volume/netapp/pom.xml | 65 ++++++ .../driver/NetAppPrimaryDatastoreDriver.java | 191 ++++++++++++++++++ .../NetAppPrimaryDatastoreLifecycle.java | 137 +++++++++++++ .../cloudstack/storage/ontap/Volume.java | 25 +++ .../NetAppPrimaryDatastoreProvider.java | 64 ++++++ .../cloudstack/storage/util/NetAppUtil.java | 127 ++++++++++++ .../storage-volume-netapp/module.properties | 18 ++ .../spring-storage-volume-netapp-context.xml | 33 +++ 16 files changed, 995 insertions(+) create mode 100644 plugins/storage/image/netapp/pom.xml create mode 100644 plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppSecondaryDatastoreDriver.java create mode 100644 plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppSecondaryDatastoreLifecycle.java create mode 100644 plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java create mode 100644 plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/module.properties create mode 100644 plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/spring-storage-image-netapp-context.xml create mode 100644 plugins/storage/volume/netapp/pom.xml create mode 100644 plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java create mode 100644 plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java create mode 100644 plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/ontap/Volume.java create mode 100644 plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java create mode 100644 plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java create mode 100644 plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/module.properties create mode 100644 plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/spring-storage-volume-netapp-context.xml diff --git a/client/pom.xml b/client/pom.xml index 81e2b7809345..4b0888896a84 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -125,6 +125,16 @@ org.apache.cloudstack cloud-plugin-storage-volume-solidfire ${project.version} + + + org.apache.cloudstack + cloud-plugin-storage-volume-netapp + ${project.version} + + + org.apache.cloudstack + cloud-plugin-storage-image-netapp + ${project.version} org.apache.cloudstack diff --git a/plugins/pom.xml b/plugins/pom.xml index 6a1a13fb5b1e..f6a6b3fb9f0c 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -124,9 +124,11 @@ storage/image/s3 storage/image/sample storage/image/swift + storage/image/netapp storage/volume/cloudbyte storage/volume/datera storage/volume/default + storage/volume/netapp storage/volume/nexenta storage/volume/sample storage/volume/solidfire diff --git a/plugins/storage/image/netapp/pom.xml b/plugins/storage/image/netapp/pom.xml new file mode 100644 index 000000000000..7f7d4ef2d73b --- /dev/null +++ b/plugins/storage/image/netapp/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + cloud-plugin-storage-image-netapp + Apache CloudStack Plugin - Storage Image NetApp Provider + + org.apache.cloudstack + cloudstack-plugins + 4.19.4.0-SNAPSHOT + ../../../pom.xml + + + + org.apache.cloudstack + cloud-plugin-storage-image-default + ${project.version} + + + org.apache.cloudstack + cloud-engine-storage-image + ${project.version} + + + + + + maven-surefire-plugin + + true + + + + integration-test + + test + + + + + + + diff --git a/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppSecondaryDatastoreDriver.java b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppSecondaryDatastoreDriver.java new file mode 100644 index 000000000000..65f2d74acc22 --- /dev/null +++ b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppSecondaryDatastoreDriver.java @@ -0,0 +1,91 @@ +package org.apache.cloudstack.storage.driver; + + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DatadiskTO; +import com.cloud.host.Host; +import com.cloud.storage.Storage; +import com.cloud.storage.Upload; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.storage.image.ImageStoreDriver; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +@Component +public class NetAppSecondaryDatastoreDriver implements ImageStoreDriver { + + @Override + public Map getCapabilities() { + return null; + } + + @Override + public DataTO getTO(DataObject data) { + return null; + } + + @Override + public DataStoreTO getStoreTO(DataStore store) { + return null; + } + + @Override + public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + + } + + @Override + public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + + } + + + @Override + public boolean canCopy(DataObject srcData, DataObject destData) { + return false; + } + + @Override + public void resize(DataObject data, AsyncCompletionCallback callback) { + + } + + @Override + public String createEntityExtractUrl(DataStore store, String installPath, Storage.ImageFormat format, DataObject dataObject) { + return null; + } + + @Override + public void deleteEntityExtractUrl(DataStore store, String installPath, String url, Upload.Type entityType) { + + } + + @Override + public List getDataDiskTemplates(DataObject obj, String configurationId) { + return null; + } + + @Override + public Void createDataDiskTemplateAsync(TemplateInfo dataDiskTemplate, String path, String diskId, boolean bootable, long fileSize, AsyncCompletionCallback callback) { + return null; + } +} \ No newline at end of file diff --git a/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppSecondaryDatastoreLifecycle.java b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppSecondaryDatastoreLifecycle.java new file mode 100644 index 000000000000..3227af8cfb64 --- /dev/null +++ b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppSecondaryDatastoreLifecycle.java @@ -0,0 +1,60 @@ +package org.apache.cloudstack.storage.lifecycle; + + +import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +import org.apache.cloudstack.storage.image.store.lifecycle.ImageStoreLifeCycle; + +import java.util.Map; + +public class NetAppSecondaryDatastoreLifecycle implements ImageStoreLifeCycle { + + /** + * Creates secondary storage on NetApp storage + * @param dsInfos + * @return + */ + @Override + public DataStore initialize(Map dsInfos) { + return null; + } + + @Override + public boolean attachCluster(DataStore store, ClusterScope scope) { + return false; + } + + @Override + public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) { + return false; + } + + @Override + public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) { + return true; + } + + @Override + public boolean maintain(DataStore store) { + return true; + } + + @Override + public boolean cancelMaintain(DataStore store) { + return true; + } + + @Override + public boolean deleteDataStore(DataStore store) { + return true; + } + + @Override + public boolean migrateToObjectStore(DataStore store) { + return true; + } +} \ No newline at end of file diff --git a/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java new file mode 100644 index 000000000000..3edd5dd9c6e2 --- /dev/null +++ b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java @@ -0,0 +1,78 @@ +package org.apache.cloudstack.storage.provider; + +import com.cloud.storage.ScopeType; +import com.cloud.utils.component.ComponentContext; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider; +import org.apache.cloudstack.storage.driver.NetAppSecondaryDatastoreDriver; +import org.apache.cloudstack.storage.image.datastore.ImageStoreProviderManager; +import org.apache.cloudstack.storage.lifecycle.NetAppSecondaryDatastoreLifecycle; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Component +public class NetAppSecondaryDatastoreProvider implements ImageStoreProvider { + + private static final Logger s_logger = Logger.getLogger(NetAppSecondaryDatastoreProvider.class); + @Inject + ImageStoreProviderManager storeMgr; + private NetAppSecondaryDatastoreDriver secondaryDatastoreDriver; + private NetAppSecondaryDatastoreLifecycle secondaryDatastoreLifecycle; + + public NetAppSecondaryDatastoreProvider() { + s_logger.info("NetAppSecondaryDatastoreProvider initialized"); + } + @Override + public DataStoreLifeCycle getDataStoreLifeCycle() { + return secondaryDatastoreLifecycle; + } + + @Override + public DataStoreDriver getDataStoreDriver() { + return secondaryDatastoreDriver; + } + + @Override + public HypervisorHostListener getHostListener() { + return null; + } + + @Override + public String getName() { + return "NetApp"; + } + + @Override + public boolean configure(Map params) { + secondaryDatastoreLifecycle = ComponentContext.inject(NetAppSecondaryDatastoreLifecycle.class); + secondaryDatastoreDriver = ComponentContext.inject(NetAppSecondaryDatastoreDriver.class); + storeMgr.registerDriver(this.getName(), secondaryDatastoreDriver); + return true; + } + + @Override + public Set getTypes() { + Set types = new HashSet(); + types.add(DataStoreProviderType.IMAGE); + return types; + } + + @Override + public boolean isScopeSupported(ScopeType scope) { + if (scope == ScopeType.REGION) + return true; + return false; + } + + @Override + public boolean needDownloadSysTemplate() { + return true; + } +} \ No newline at end of file diff --git a/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/module.properties b/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/module.properties new file mode 100644 index 000000000000..f068e552090c --- /dev/null +++ b/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/module.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +name=storage-image-netapp +parent=storage diff --git a/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/spring-storage-image-netapp-context.xml b/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/spring-storage-image-netapp-context.xml new file mode 100644 index 000000000000..c268c5a6adc1 --- /dev/null +++ b/plugins/storage/image/netapp/src/main/resources/META-INF/cloudstack/storage-image-netapp/spring-storage-image-netapp-context.xml @@ -0,0 +1,33 @@ + + + + + + diff --git a/plugins/storage/volume/netapp/pom.xml b/plugins/storage/volume/netapp/pom.xml new file mode 100644 index 000000000000..4a059db6508e --- /dev/null +++ b/plugins/storage/volume/netapp/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + cloud-plugin-storage-volume-netapp + Apache CloudStack Plugin - Storage Volume NetApp Provider + + org.apache.cloudstack + cloudstack-plugins + 4.19.4.0-SNAPSHOT + ../../../pom.xml + + + + org.apache.cloudstack + cloud-plugin-storage-volume-default + ${project.version} + + + org.json + json + 20230227 + + + org.apache.cloudstack + cloud-engine-storage-volume + ${project.version} + + + + + + maven-surefire-plugin + + true + + + + integration-test + + test + + + + + + + diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java new file mode 100644 index 000000000000..efc3c43d6c69 --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java @@ -0,0 +1,191 @@ +package org.apache.cloudstack.storage.driver; + + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.host.Host; +import com.cloud.storage.Storage; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; +import com.cloud.utils.Pair; +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class NetAppPrimaryDatastoreDriver implements PrimaryDataStoreDriver { + + private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreDriver.class); + @Override + public Map getCapabilities() { + s_logger.trace("NetAppPrimaryDatastoreDriver: getCapabilities: Called"); + Map mapCapabilities = new HashMap<>(); + + mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_REVERT_VOLUME_TO_SNAPSHOT.toString(), Boolean.TRUE.toString()); + + return mapCapabilities; + } + + @Override + public DataTO getTO(DataObject data) { + return null; + } + + @Override + public DataStoreTO getStoreTO(DataStore store) { + return null; + } + + @Override + public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + + s_logger.trace("NetAppPrimaryDatastoreDriver: createAsync: Store: "+store+", data: "+data); + } + + @Override + public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + + } + + @Override + public boolean canCopy(DataObject srcData, DataObject destData) { + return false; + } + + @Override + public void resize(DataObject data, AsyncCompletionCallback callback) { + + } + + @Override + public ChapInfo getChapInfo(DataObject dataObject) { + return null; + } + + @Override + public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) { + return true; + } + + @Override + public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) { + + } + + @Override + public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool storagePool) { + return 0; + } + + @Override + public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { + return 0; + } + + @Override + public long getUsedBytes(StoragePool storagePool) { + return 0; + } + + @Override + public long getUsedIops(StoragePool storagePool) { + return 0; + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + + } + + @Override + public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback callback) { + + } + + @Override + public void handleQualityOfServiceForVolumeMigration(VolumeInfo volumeInfo, QualityOfServiceState qualityOfServiceState) { + + } + + @Override + public boolean canProvideStorageStats() { + return true; + } + + @Override + public Pair getStorageStats(StoragePool storagePool) { + return null; + } + + @Override + public boolean canProvideVolumeStats() { + return true; + } + + @Override + public Pair getVolumeStats(StoragePool storagePool, String volumeId) { + return null; + } + + @Override + public boolean canHostAccessStoragePool(Host host, StoragePool pool) { + return true; + } + + @Override + public boolean isVmInfoNeeded() { + return true; + } + + @Override + public void provideVmInfo(long vmId, long volumeId) { + + } + + @Override + public boolean isVmTagsNeeded(String tagKey) { + return true; + } + + @Override + public void provideVmTags(long vmId, long volumeId, String tagValue) { + + } + + @Override + public boolean isStorageSupportHA(Storage.StoragePoolType type) { + return true; + } + + @Override + public void detachVolumeFromAllStorageNodes(Volume volume) { + + } +} \ No newline at end of file diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java new file mode 100644 index 000000000000..cb518f5d1b3b --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java @@ -0,0 +1,137 @@ +package org.apache.cloudstack.storage.lifecycle; + + +import com.cloud.agent.api.StoragePoolInfo; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.StoragePool; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +import org.apache.cloudstack.storage.util.NetAppUtil; +import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.Map; + +public class NetAppPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle { + + private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreLifecycle.class); + + @Inject + private PrimaryDataStoreHelper primaryDataStoreHelper; + /** + * Creates primary storage on NetApp storage + * @param dsInfos + * @return + */ + @Override + public DataStore initialize(Map dsInfos) { + s_logger.info("NetAppPrimaryDatastoreLifecycle: initialize: params: dsInfos: "+ dsInfos); + String url = (String)dsInfos.get("url"); + Long zoneId = (Long)dsInfos.get("zoneId"); + Long podId = (Long)dsInfos.get("podId"); + Long clusterId = (Long)dsInfos.get("clusterId"); + String storagePoolName = (String)dsInfos.get("name"); + String providerName = (String)dsInfos.get("providerName"); + Long capacityBytes = (Long)dsInfos.get("capacityBytes"); + Long capacityIops = (Long)dsInfos.get("capacityIops"); + String tags = (String)dsInfos.get("tags"); + Boolean isTagARule = (Boolean) dsInfos.get("isTagARule"); + Map details = (Map)dsInfos.get("details"); + + s_logger.info("values: "+dsInfos.toString()); + + if (podId == null ^ clusterId == null) { + throw new CloudRuntimeException("Both POD and cluster values together should either be specified or not."); + } + + if (url == null || url.trim().equals("")) { + throw new IllegalArgumentException("Valid 'URL' value must be present."); + } + + if (capacityBytes == null || capacityBytes <= 0) { + throw new IllegalArgumentException("'capacityBytes' must be present and greater than 0."); + } + + if (capacityIops == null || capacityIops <= 0) { + throw new IllegalArgumentException("'capacityIops' must be present and greater than 0."); + } + + PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters(); + NetAppUtil util = new NetAppUtil(); + //util.POST() + /* + Instantiating for ONTAP connection would come here. + */ + + + // this adds a row in the cloud.storage_pool table for this SolidFire cluster + return primaryDataStoreHelper.createPrimaryDataStore(parameters); + + } + + @Override + public boolean attachCluster(DataStore store, ClusterScope scope) { + return false; + } + + @Override + public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) { + return false; + } + + @Override + public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) { + return false; + } + + @Override + public boolean maintain(DataStore store) { + return true; + } + + @Override + public boolean cancelMaintain(DataStore store) { + return true; + } + + @Override + public boolean deleteDataStore(DataStore store) { + return true; + } + + @Override + public boolean migrateToObjectStore(DataStore store) { + return true; + } + + @Override + public void updateStoragePool(StoragePool storagePool, Map details) { + + } + + @Override + public void enableStoragePool(DataStore store) { + + } + + @Override + public void disableStoragePool(DataStore store) { + + } + + @Override + public void changeStoragePoolScopeToZone(DataStore store, ClusterScope clusterScope, Hypervisor.HypervisorType hypervisorType) { + + } + + @Override + public void changeStoragePoolScopeToCluster(DataStore store, ClusterScope clusterScope, Hypervisor.HypervisorType hypervisorType) { + + } +} \ No newline at end of file diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/ontap/Volume.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/ontap/Volume.java new file mode 100644 index 000000000000..d7b008d16019 --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/ontap/Volume.java @@ -0,0 +1,25 @@ +package org.apache.cloudstack.storage.ontap; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Volume { + @JsonProperty("name") + private Boolean name; + + @JsonProperty("size") + private long size; + + @JsonProperty("qos") + private long qos; + + @JsonProperty("svmName") + private String svmName; + + @JsonProperty("cluster") + private String cluster; + +} diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java new file mode 100644 index 000000000000..df157aa5fc8e --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java @@ -0,0 +1,64 @@ +package org.apache.cloudstack.storage.provider; + + +import com.cloud.utils.component.ComponentContext; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; +import org.apache.cloudstack.storage.driver.NetAppPrimaryDatastoreDriver; +import org.apache.cloudstack.storage.lifecycle.NetAppPrimaryDatastoreLifecycle; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +@Component +public class NetAppPrimaryDatastoreProvider implements PrimaryDataStoreProvider { + + private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreProvider.class); + private NetAppPrimaryDatastoreDriver primaryDatastoreDriver; + private NetAppPrimaryDatastoreLifecycle primaryDatastoreLifecycle; + + public NetAppPrimaryDatastoreProvider() { + s_logger.info("NetAppPrimaryDatastoreProvider initialized"); + } + @Override + public DataStoreLifeCycle getDataStoreLifeCycle() { + return primaryDatastoreLifecycle; + } + + @Override + public DataStoreDriver getDataStoreDriver() { + return primaryDatastoreDriver; + } + + @Override + public HypervisorHostListener getHostListener() { + return null; + } + + @Override + public String getName() { + s_logger.trace("NetAppPrimaryDatastoreProvider: getName: Called"); + return "NetAppStorage"; + } + + @Override + public boolean configure(Map params) { + s_logger.trace("NetAppPrimaryDatastoreProvider: configure: Called"); + primaryDatastoreDriver = ComponentContext.inject(NetAppPrimaryDatastoreDriver.class); + primaryDatastoreLifecycle = ComponentContext.inject(NetAppPrimaryDatastoreLifecycle.class); + return true; + } + + @Override + public Set getTypes() { + s_logger.trace("NetAppPrimaryDatastoreProvider: getTypes: Called"); + Set typeSet = new HashSet(); + typeSet.add(DataStoreProviderType.PRIMARY); + return typeSet; + } +} \ No newline at end of file diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java new file mode 100644 index 000000000000..5055c264de19 --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java @@ -0,0 +1,127 @@ +package org.apache.cloudstack.storage.util; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.HttpEntity; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.TrustAllStrategy; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.log4j.Logger; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.Map; + +public class NetAppUtil { + + private static final Logger s_logger = Logger.getLogger(NetAppUtil.class); + private final long connTimeout = 5000; + private final boolean skipTlsValidation = false; + static final ObjectMapper mapper = new ObjectMapper(); + private CloseableHttpClient client; + public T POST(String path, Object input, final TypeReference type) { + CloseableHttpResponse response = null; + String managementIp = ""; + try { + HttpPost request = new HttpPost("https://10.196.64.59/api/storage/volumes"); + request.addHeader("Content-Type", "application/json"); + request.addHeader("Accept", "application/json"); + //request.addHeader("X-auth-token", accessToken); + request.addHeader("username", "admin"); + request.addHeader("password", "netapp1!"); + + if (input != null) { + try { + String data = mapper.writeValueAsString(input); + request.setEntity(new StringEntity(data)); + } catch (IOException e) { + throw new RuntimeException("Error processing request payload", e); + } + } + + try (CloseableHttpClient client = HttpClients.createDefault()) { + response = client.execute(request); + + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode == 200 || statusCode == 201) { + if (type != null) { + if (type.getType().getTypeName().equals(String.class.getName())) { + return (T) response.getFirstHeader("Location").getValue(); + } else { + HttpEntity entity = response.getEntity(); + return mapper.readValue(entity.getContent(), type); + } + } + } else if (statusCode == 400) { + Map payload = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {}); + throw new RuntimeException("Invalid request error 400: " + payload); + } else if (statusCode == 401 || statusCode == 403) { + throw new RuntimeException("Authentication or Authorization failed"); + } else { + Map payload = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {}); + throw new RuntimeException("Invalid request error " + statusCode + ": " + payload); + } + } catch (IOException e) { + throw new RuntimeException("Error sending request", e); + } + } finally { + if (response != null) { + try { + response.close(); + } catch (IOException e) { + System.out.println("Error closing response"); + } + } + } + + return null; + } + + public CloseableHttpClient getClient() { + if (client == null) { + RequestConfig config = RequestConfig.custom() + .setConnectTimeout((int) connTimeout) + .setConnectionRequestTimeout((int) connTimeout) + .setSocketTimeout((int) connTimeout).build(); + + HostnameVerifier verifier = null; + SSLContext sslContext = null; + + /** + * we have not configured for tls validations for now. + */ + if (skipTlsValidation) { + try { + verifier = NoopHostnameVerifier.INSTANCE; + sslContext = new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(); + } catch (KeyManagementException e) { + throw new CloudRuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException(e); + } catch (KeyStoreException e) { + throw new CloudRuntimeException(e); + } + } + + client = HttpClients.custom() + .setDefaultRequestConfig(config) + .setSSLHostnameVerifier(verifier) + .setSSLContext(sslContext) + .build(); + } + return client; + } + + +} diff --git a/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/module.properties b/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/module.properties new file mode 100644 index 000000000000..7f3779bb5eb9 --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/module.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +name=storage-volume-netapp +parent=storage diff --git a/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/spring-storage-volume-netapp-context.xml b/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/spring-storage-volume-netapp-context.xml new file mode 100644 index 000000000000..25bb004ca6fd --- /dev/null +++ b/plugins/storage/volume/netapp/src/main/resources/META-INF/cloudstack/storage-volume-netapp/spring-storage-volume-netapp-context.xml @@ -0,0 +1,33 @@ + + + + + + From ba8249c99ce615d6e9c59b6730ea9d71580ef11f Mon Sep 17 00:00:00 2001 From: "Jain, Rajiv" Date: Sat, 16 Aug 2025 08:41:38 +0530 Subject: [PATCH 2/4] added code for local testing --- client/pom.xml | 2 +- plugins/storage/image/netapp/pom.xml | 2 +- .../NetAppSecondaryDatastoreProvider.java | 5 +- plugins/storage/volume/netapp/pom.xml | 2 +- .../driver/NetAppPrimaryDatastoreDriver.java | 5 +- .../NetAppPrimaryDatastoreLifecycle.java | 50 +++++- .../NetAppPrimaryDatastoreProvider.java | 5 +- .../cloudstack/storage/util/NetAppUtil.java | 156 ++++++------------ ui/public/locales/es.json | 3 + ui/src/views/infra/AddPrimaryStorage.vue | 39 ++++- 10 files changed, 143 insertions(+), 126 deletions(-) diff --git a/client/pom.xml b/client/pom.xml index 4b0888896a84..6db8b37342e5 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -126,7 +126,7 @@ cloud-plugin-storage-volume-solidfire ${project.version} - + org.apache.cloudstack cloud-plugin-storage-volume-netapp ${project.version} diff --git a/plugins/storage/image/netapp/pom.xml b/plugins/storage/image/netapp/pom.xml index 7f7d4ef2d73b..4e6109b0a98c 100644 --- a/plugins/storage/image/netapp/pom.xml +++ b/plugins/storage/image/netapp/pom.xml @@ -7,7 +7,7 @@ org.apache.cloudstack cloudstack-plugins - 4.19.4.0-SNAPSHOT + 4.21.0.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java index 3edd5dd9c6e2..0b880f59a92c 100644 --- a/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java +++ b/plugins/storage/image/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppSecondaryDatastoreProvider.java @@ -9,7 +9,8 @@ import org.apache.cloudstack.storage.driver.NetAppSecondaryDatastoreDriver; import org.apache.cloudstack.storage.image.datastore.ImageStoreProviderManager; import org.apache.cloudstack.storage.lifecycle.NetAppSecondaryDatastoreLifecycle; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import javax.inject.Inject; @@ -20,7 +21,7 @@ @Component public class NetAppSecondaryDatastoreProvider implements ImageStoreProvider { - private static final Logger s_logger = Logger.getLogger(NetAppSecondaryDatastoreProvider.class); + private static final Logger s_logger = (Logger)LogManager.getLogger(NetAppSecondaryDatastoreProvider.class); @Inject ImageStoreProviderManager storeMgr; private NetAppSecondaryDatastoreDriver secondaryDatastoreDriver; diff --git a/plugins/storage/volume/netapp/pom.xml b/plugins/storage/volume/netapp/pom.xml index 4a059db6508e..7762a4354401 100644 --- a/plugins/storage/volume/netapp/pom.xml +++ b/plugins/storage/volume/netapp/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack-plugins - 4.19.4.0-SNAPSHOT + 4.21.0.0-SNAPSHOT ../../../pom.xml diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java index efc3c43d6c69..ccc38fc75fd4 100644 --- a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/driver/NetAppPrimaryDatastoreDriver.java @@ -20,7 +20,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -29,7 +30,7 @@ @Component public class NetAppPrimaryDatastoreDriver implements PrimaryDataStoreDriver { - private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreDriver.class); + private static final Logger s_logger = (Logger)LogManager.getLogger(NetAppPrimaryDatastoreDriver.class); @Override public Map getCapabilities() { s_logger.trace("NetAppPrimaryDatastoreDriver: getCapabilities: Called"); diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java index cb518f5d1b3b..fc04fe1d2ecd 100644 --- a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/lifecycle/NetAppPrimaryDatastoreLifecycle.java @@ -3,6 +3,7 @@ import com.cloud.agent.api.StoragePoolInfo; import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePool; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; @@ -13,17 +14,22 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.storage.util.NetAppUtil; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import javax.inject.Inject; import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; public class NetAppPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle { - private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreLifecycle.class); + private static final Logger s_logger = (Logger)LogManager.getLogger(NetAppPrimaryDatastoreLifecycle.class); @Inject private PrimaryDataStoreHelper primaryDataStoreHelper; + NetAppUtil util = new NetAppUtil(); /** * Creates primary storage on NetApp storage * @param dsInfos @@ -62,15 +68,45 @@ public DataStore initialize(Map dsInfos) { throw new IllegalArgumentException("'capacityIops' must be present and greater than 0."); } + + CompletableFuture volumeCreatedFuture = util.createOntapVolume(url, storagePoolName, capacityBytes, details); + volumeCreatedFuture.thenAccept(volumeCreated -> { + if (volumeCreated) { + s_logger.info("Volume created"); + } else { + throw new RuntimeException("Volume creation failed."); + } + }); + + PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters(); - NetAppUtil util = new NetAppUtil(); - //util.POST() - /* - Instantiating for ONTAP connection would come here. - */ + + String input = "{ \"aggregates\": [ { \"name\": \"sti246_vsim_ocvs040a_aggr1\" } ], \"name\": \"vol_test2\", \"size\": 1024000, \"svm\": { \"name\": \"vs0\" }}"; // this adds a row in the cloud.storage_pool table for this SolidFire cluster + parameters.setCapacityBytes(10240000); + parameters.setHost("10.196.38.171"); + // parameters.setPort(); + parameters.setPath("https://10.196.38.171/"); + parameters.setType(Storage.StoragePoolType.Iscsi); + parameters.setUuid(UUID.randomUUID().toString()); + parameters.setZoneId(zoneId); + parameters.setPodId(podId); + parameters.setClusterId(clusterId); + parameters.setName(storagePoolName); + parameters.setProviderName(providerName); + parameters.setManaged(true); + parameters.setCapacityBytes(capacityBytes); + parameters.setUsedBytes(0); + parameters.setCapacityIops(capacityIops); + parameters.setHypervisorType(Hypervisor.HypervisorType.KVM); + parameters.setTags(tags); + parameters.setIsTagARule(isTagARule); + parameters.setDetails(details); + details.put("username", "admin"); + details.put("password", "netapp1!"); + return primaryDataStoreHelper.createPrimaryDataStore(parameters); } diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java index df157aa5fc8e..b433a8080123 100644 --- a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/provider/NetAppPrimaryDatastoreProvider.java @@ -8,7 +8,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; import org.apache.cloudstack.storage.driver.NetAppPrimaryDatastoreDriver; import org.apache.cloudstack.storage.lifecycle.NetAppPrimaryDatastoreLifecycle; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.Logger; import org.springframework.stereotype.Component; import java.util.HashSet; @@ -18,7 +19,7 @@ @Component public class NetAppPrimaryDatastoreProvider implements PrimaryDataStoreProvider { - private static final Logger s_logger = Logger.getLogger(NetAppPrimaryDatastoreProvider.class); + private static final Logger s_logger = (Logger)LogManager.getLogger(NetAppPrimaryDatastoreProvider.class); private NetAppPrimaryDatastoreDriver primaryDatastoreDriver; private NetAppPrimaryDatastoreLifecycle primaryDatastoreLifecycle; diff --git a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java index 5055c264de19..77152cf8de6b 100644 --- a/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java +++ b/plugins/storage/volume/netapp/src/main/java/org/apache/cloudstack/storage/util/NetAppUtil.java @@ -1,127 +1,67 @@ package org.apache.cloudstack.storage.util; -import com.cloud.utils.exception.CloudRuntimeException; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.HttpEntity; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.TrustAllStrategy; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.concurrent.CompletableFuture; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; import java.util.Map; public class NetAppUtil { - private static final Logger s_logger = Logger.getLogger(NetAppUtil.class); + private static final Logger s_logger = (Logger) LogManager.getLogger(NetAppUtil.class); private final long connTimeout = 5000; private final boolean skipTlsValidation = false; static final ObjectMapper mapper = new ObjectMapper(); - private CloseableHttpClient client; - public T POST(String path, Object input, final TypeReference type) { - CloseableHttpResponse response = null; - String managementIp = ""; - try { - HttpPost request = new HttpPost("https://10.196.64.59/api/storage/volumes"); - request.addHeader("Content-Type", "application/json"); - request.addHeader("Accept", "application/json"); - //request.addHeader("X-auth-token", accessToken); - request.addHeader("username", "admin"); - request.addHeader("password", "netapp1!"); - - if (input != null) { - try { - String data = mapper.writeValueAsString(input); - request.setEntity(new StringEntity(data)); - } catch (IOException e) { - throw new RuntimeException("Error processing request payload", e); - } - } - - try (CloseableHttpClient client = HttpClients.createDefault()) { - response = client.execute(request); - - int statusCode = response.getStatusLine().getStatusCode(); - if (statusCode == 200 || statusCode == 201) { - if (type != null) { - if (type.getType().getTypeName().equals(String.class.getName())) { - return (T) response.getFirstHeader("Location").getValue(); - } else { - HttpEntity entity = response.getEntity(); - return mapper.readValue(entity.getContent(), type); - } - } - } else if (statusCode == 400) { - Map payload = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {}); - throw new RuntimeException("Invalid request error 400: " + payload); - } else if (statusCode == 401 || statusCode == 403) { - throw new RuntimeException("Authentication or Authorization failed"); - } else { - Map payload = mapper.readValue(response.getEntity().getContent(), new TypeReference>() {}); - throw new RuntimeException("Invalid request error " + statusCode + ": " + payload); - } - } catch (IOException e) { - throw new RuntimeException("Error sending request", e); - } - } finally { - if (response != null) { - try { - response.close(); - } catch (IOException e) { - System.out.println("Error closing response"); - } - } - } - - return null; - } + // Use shared HttpClient instance + HttpClient client = HttpClient.newHttpClient(); - public CloseableHttpClient getClient() { - if (client == null) { - RequestConfig config = RequestConfig.custom() - .setConnectTimeout((int) connTimeout) - .setConnectionRequestTimeout((int) connTimeout) - .setSocketTimeout((int) connTimeout).build(); + public CompletableFuture createOntapVolume(String url, String volumeName, Long capacityBytes, Map details) { + HttpRequest request = null; + try { + String payload1 = "{ \"aggregates\": [ " + + " { " + + " \"name\": \"sti246_vsim_ocvs040a_aggr1\" " + + " } " + + " ], " + + " \"name\": \"" + volumeName + "\", " + + " \"size\": " + capacityBytes + ", " + + " \"svm\": { " + + " \"name\": \"vs0\" " + + " }" + + " }"; - HostnameVerifier verifier = null; - SSLContext sslContext = null; + // Prepare payload + /*Map payload = Map.of( + "name", volumeName, + "size", capacityBytes, + "details", details + );*/ - /** - * we have not configured for tls validations for now. - */ - if (skipTlsValidation) { - try { - verifier = NoopHostnameVerifier.INSTANCE; - sslContext = new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(); - } catch (KeyManagementException e) { - throw new CloudRuntimeException(e); - } catch (NoSuchAlgorithmException e) { - throw new CloudRuntimeException(e); - } catch (KeyStoreException e) { - throw new CloudRuntimeException(e); - } - } + ObjectMapper mapper = new ObjectMapper(); + //String jsonPayload = mapper.writeValueAsString(payload); - client = HttpClients.custom() - .setDefaultRequestConfig(config) - .setSSLHostnameVerifier(verifier) - .setSSLContext(sslContext) + // Build HTTP request + request = HttpRequest.newBuilder() + .uri(URI.create("https://10.196.38.171/api/storage/volumes")) + .header("Content-Type", "application/json") + .header("username", "admin") + .header("password", "netapp1!") + .POST(HttpRequest.BodyPublishers.ofString(payload1)) .build(); + + // Send async request + return client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> response.statusCode() == 200 || response.statusCode() == 201); + } catch (Exception e) { + CompletableFuture failedFuture = new CompletableFuture<>(); + failedFuture.completeExceptionally(e); + return failedFuture; } - return client; } - - } + diff --git a/ui/public/locales/es.json b/ui/public/locales/es.json index ab57c9515316..2514ac33513b 100644 --- a/ui/public/locales/es.json +++ b/ui/public/locales/es.json @@ -859,6 +859,9 @@ "label.presetup": "PreConfiguraci\u00f3n", "label.prev": "Anterior", "label.previous": "Previo", +"label.netapp.username.tooltip": "The username with edit privileges", +"label.netapp.password": "The password for the NetApp storage array", +"label.netapp.url.tooltip": "URL designating the NetApp storage endpoint, formatted as: http[s]://HOSTNAME:PORT/", "label.primary.storage": "Almacenamiento Primario", "label.primary.storage.allocated": "Almacenamiento Primario Asignado", "label.primary.storage.used": "Almacenamiento Primario Usado", diff --git a/ui/src/views/infra/AddPrimaryStorage.vue b/ui/src/views/infra/AddPrimaryStorage.vue index d46396bbb3a5..2da192eb6a85 100644 --- a/ui/src/views/infra/AddPrimaryStorage.vue +++ b/ui/src/views/infra/AddPrimaryStorage.vue @@ -242,7 +242,7 @@ -
+