Skip to content

Commit 9bf90ea

Browse files
feature/CSTACKEX-46: Create, Delete iSCSI type Cloudstack volumes, Enter, Cancel Maintenance mode
Create, Delete iSCSI type Cloudstack volumes, Enter, Cancel Maintenance mode and changes added in the community PR
1 parent 8a2c7fb commit 9bf90ea

File tree

21 files changed

+3953
-279
lines changed

21 files changed

+3953
-279
lines changed

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.HashMap;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.nio.file.Files;
23+
import java.nio.file.Paths;
2224

2325
import org.apache.cloudstack.utils.qemu.QemuImg;
2426
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
@@ -96,10 +98,15 @@ public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<S
9698
String result = iScsiAdmCmd.execute();
9799

98100
if (result != null) {
99-
logger.debug("Failed to add iSCSI target " + volumeUuid);
100-
System.out.println("Failed to add iSCSI target " + volumeUuid);
101+
// Node record may already exist from a previous run; accept and proceed
102+
if (isNonFatalNodeCreate(result)) {
103+
logger.debug("iSCSI node already exists for {}@{}:{}, proceeding", getIqn(volumeUuid), pool.getSourceHost(), pool.getSourcePort());
104+
} else {
105+
logger.debug("Failed to add iSCSI target " + volumeUuid);
106+
System.out.println("Failed to add iSCSI target " + volumeUuid);
101107

102-
return false;
108+
return false;
109+
}
103110
} else {
104111
logger.debug("Successfully added iSCSI target " + volumeUuid);
105112
System.out.println("Successfully added to iSCSI target " + volumeUuid);
@@ -123,21 +130,28 @@ public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<S
123130
}
124131
}
125132

126-
// ex. sudo iscsiadm -m node -T iqn.2012-03.com.test:volume1 -p 192.168.233.10:3260 --login
127-
iScsiAdmCmd = new Script(true, "iscsiadm", 0, logger);
133+
final String host = pool.getSourceHost();
134+
final int port = pool.getSourcePort();
135+
final String iqn = getIqn(volumeUuid);
128136

137+
// Always try to login; treat benign outcomes as success (idempotent)
138+
iScsiAdmCmd = new Script(true, "iscsiadm", 0, logger);
129139
iScsiAdmCmd.add("-m", "node");
130-
iScsiAdmCmd.add("-T", getIqn(volumeUuid));
131-
iScsiAdmCmd.add("-p", pool.getSourceHost() + ":" + pool.getSourcePort());
140+
iScsiAdmCmd.add("-T", iqn);
141+
iScsiAdmCmd.add("-p", host + ":" + port);
132142
iScsiAdmCmd.add("--login");
133143

134144
result = iScsiAdmCmd.execute();
135145

136146
if (result != null) {
137-
logger.debug("Failed to log in to iSCSI target " + volumeUuid);
138-
System.out.println("Failed to log in to iSCSI target " + volumeUuid);
147+
if (isNonFatalLogin(result)) {
148+
logger.debug("iSCSI login returned benign message for {}@{}:{}: {}", iqn, host, port, result);
149+
} else {
150+
logger.debug("Failed to log in to iSCSI target " + volumeUuid + ": " + result);
151+
System.out.println("Failed to log in to iSCSI target " + volumeUuid);
139152

140-
return false;
153+
return false;
154+
}
141155
} else {
142156
logger.debug("Successfully logged in to iSCSI target " + volumeUuid);
143157
System.out.println("Successfully logged in to iSCSI target " + volumeUuid);
@@ -158,8 +172,23 @@ public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<S
158172
return true;
159173
}
160174

175+
// Removed sessionExists() call to avoid noisy sudo/iscsiadm session queries that may fail on some setups
176+
177+
private boolean isNonFatalLogin(String result) {
178+
if (result == null) return true;
179+
String msg = result.toLowerCase();
180+
// Accept messages where the session already exists
181+
return msg.contains("already present") || msg.contains("already logged in") || msg.contains("session exists");
182+
}
183+
184+
private boolean isNonFatalNodeCreate(String result) {
185+
if (result == null) return true;
186+
String msg = result.toLowerCase();
187+
return msg.contains("already exists") || msg.contains("database exists") || msg.contains("exists");
188+
}
189+
161190
private void waitForDiskToBecomeAvailable(String volumeUuid, KVMStoragePool pool) {
162-
int numberOfTries = 10;
191+
int numberOfTries = 30;
163192
int timeBetweenTries = 1000;
164193

165194
while (getPhysicalDisk(volumeUuid, pool).getSize() == 0 && numberOfTries > 0) {
@@ -238,6 +267,15 @@ public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) {
238267
}
239268

240269
private long getDeviceSize(String deviceByPath) {
270+
try {
271+
if (!Files.exists(Paths.get(deviceByPath))) {
272+
logger.debug("Device by-path does not exist yet: " + deviceByPath);
273+
return 0L;
274+
}
275+
} catch (Exception ignore) {
276+
// If FS check fails for any reason, fall back to blockdev call
277+
}
278+
241279
Script iScsiAdmCmd = new Script(true, "blockdev", 0, logger);
242280

243281
iScsiAdmCmd.add("--getsize64", deviceByPath);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
# Apache CloudStack - NetApp ONTAP Storage Plugin
21+
22+
## Overview
23+
24+
The NetApp ONTAP Storage Plugin provides integration between Apache CloudStack and NetApp ONTAP storage systems. This plugin enables CloudStack to provision and manage primary storage on ONTAP clusters, supporting both NAS (NFS) and SAN (iSCSI) protocols.
25+
26+
## Features
27+
28+
- **Primary Storage Support**: Provision and manage primary storage pools on NetApp ONTAP
29+
- **Multiple Protocols**: Support for NFS 3.0 and iSCSI protocols
30+
- **Unified Storage**: Integration with traditional ONTAP unified storage architecture
31+
- **KVM Hypervisor Support**: Supports KVM hypervisor environments
32+
- **Managed Storage**: Operates as managed storage with full lifecycle management
33+
- **Flexible Scoping**: Support for Zone-wide and Cluster-scoped storage pools
34+
35+
## Architecture
36+
37+
### Component Structure
38+
39+
| Package | Description |
40+
|---------|-------------------------------------------------------|
41+
| `driver` | Primary datastore driver implementation |
42+
| `feign` | REST API clients and data models for ONTAP operations |
43+
| `lifecycle` | Storage pool lifecycle management |
44+
| `listener` | Host connection event handlers |
45+
| `provider` | Main provider and strategy factory |
46+
| `service` | ONTAP Storage strategy implementations (NAS/SAN) |
47+
| `utils` | Constants and helper utilities |
48+
49+
## Requirements
50+
51+
### ONTAP Requirements
52+
53+
- NetApp ONTAP 9.15.1 or higher
54+
- Storage Virtual Machine (SVM) configured with appropriate protocols enabled
55+
- Management LIF accessible from CloudStack management server
56+
- Data LIF(s) accessible from hypervisor hosts and are of IPv4 type
57+
- Aggregates assigned to the SVM with sufficient capacity
58+
59+
### CloudStack Requirements
60+
61+
- Apache CloudStack current version or higher
62+
- KVM hypervisor hosts
63+
- For iSCSI: Hosts must have iSCSI initiator configured with valid IQN
64+
- For NFS: Hosts must have NFS client packages installed
65+
66+
### Minimum Volume Size
67+
68+
ONTAP requires a minimum volume size of **1.56 GB** (1,677,721,600 bytes). The plugin will automatically adjust requested sizes below this threshold.
69+
70+
## Configuration
71+
72+
### Storage Pool Creation Parameters
73+
74+
When creating an ONTAP primary storage pool, provide the following details in the URL field (semicolon-separated key=value pairs):
75+
76+
| Parameter | Required | Description |
77+
|-----------|----------|-------------|
78+
| `username` | Yes | ONTAP cluster admin username |
79+
| `password` | Yes | ONTAP cluster admin password |
80+
| `svmName` | Yes | Storage Virtual Machine name |
81+
| `protocol` | Yes | Storage protocol (`NFS3` or `ISCSI`) |
82+
| `managementLIF` | Yes | ONTAP cluster management LIF IP address |
83+
84+
### Example URL Format
85+
86+
```
87+
username=admin;password=secretpass;svmName=svm1;protocol=ISCSI;managementLIF=192.168.1.100
88+
```
89+
90+
## Port Configuration
91+
92+
| Protocol | Default Port |
93+
|----------|--------------|
94+
| NFS | 2049 |
95+
| iSCSI | 3260 |
96+
| ONTAP Management API | 443 (HTTPS) |
97+
98+
## Limitations
99+
100+
- Supports only **KVM** hypervisor
101+
- Supports only **Unified ONTAP** storage (disaggregated not supported)
102+
- Supports only **NFS3** and **iSCSI** protocols
103+
- IPv6 type and FQDN LIFs are not supported
104+
105+
## Troubleshooting
106+
107+
### Common Issues
108+
109+
1. **Connection Failures**
110+
- Verify management LIF is reachable from CloudStack management server
111+
- Check firewall rules for port 443
112+
113+
2. **Protocol Errors**
114+
- Ensure the protocol (NFS/iSCSI) is enabled on the SVM
115+
- Verify Data LIFs are configured for the protocol
116+
117+
3. **Capacity Errors**
118+
- Check aggregate space availability
119+
- Ensure requested volume size meets minimum requirements (1.56 GB)
120+
121+
4. **Host Connection Issues**
122+
- For iSCSI: Verify host IQN is properly configured in host's storage URL
123+
- For NFS: Ensure NFS client is installed and running

plugins/storage/volume/ontap/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
<properties>
3131
<spring-cloud.version>2021.0.7</spring-cloud.version>
3232
<openfeign.version>11.0</openfeign.version>
33-
<json.version>20230227</json.version>
3433
<httpclient.version>4.5.14</httpclient.version>
3534
<swagger-annotations.version>1.6.2</swagger-annotations.version>
3635
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
@@ -55,11 +54,6 @@
5554
<artifactId>cloud-plugin-storage-volume-default</artifactId>
5655
<version>${project.version}</version>
5756
</dependency>
58-
<dependency>
59-
<groupId>org.json</groupId>
60-
<artifactId>json</artifactId>
61-
<version>${json.version}</version>
62-
</dependency>
6357
<dependency>
6458
<groupId>io.github.openfeign</groupId>
6559
<artifactId>feign-core</artifactId>

0 commit comments

Comments
 (0)