1 /*
2 * storage_backend_iscsi.c: storage backend for iSCSI handling
3 *
4 * Copyright (C) 2007-2016 Red Hat, Inc.
5 * Copyright (C) 2007-2008 Daniel P. Berrange
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28
29 #include "datatypes.h"
30 #include "driver.h"
31 #include "storage_backend_iscsi.h"
32 #include "viralloc.h"
33 #include "vircommand.h"
34 #include "virerror.h"
35 #include "virfile.h"
36 #include "viriscsi.h"
37 #include "viridentity.h"
38 #include "virlog.h"
39 #include "virobject.h"
40 #include "virstring.h"
41 #include "viruuid.h"
42 #include "virsecret.h"
43 #include "storage_util.h"
44 #include "virutil.h"
45 #include "virsecureerase.h"
46
47 #define VIR_FROM_THIS VIR_FROM_STORAGE
48
49 VIR_LOG_INIT("storage.storage_backend_iscsi");
50
51 #define ISCSI_DEFAULT_TARGET_PORT 3260
52
53 static char *
virStorageBackendISCSIPortal(virStoragePoolSource * source)54 virStorageBackendISCSIPortal(virStoragePoolSource *source)
55 {
56 char *portal = NULL;
57
58 if (source->nhost != 1) {
59 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
60 _("Expected exactly 1 host for the storage pool"));
61 return NULL;
62 }
63
64 if (source->hosts[0].port == 0)
65 source->hosts[0].port = ISCSI_DEFAULT_TARGET_PORT;
66
67 if (strchr(source->hosts[0].name, ':')) {
68 portal = g_strdup_printf("[%s]:%d,1",
69 source->hosts[0].name,
70 source->hosts[0].port);
71 } else {
72 portal = g_strdup_printf("%s:%d,1",
73 source->hosts[0].name,
74 source->hosts[0].port);
75 }
76
77 return portal;
78 }
79
80
81 static char *
virStorageBackendISCSISession(virStoragePoolObj * pool,bool probe)82 virStorageBackendISCSISession(virStoragePoolObj *pool,
83 bool probe)
84 {
85 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
86 return virISCSIGetSession(def->source.devices[0].path, probe);
87 }
88
89
90 static int
virStorageBackendISCSIGetHostNumber(const char * sysfs_path,uint32_t * host)91 virStorageBackendISCSIGetHostNumber(const char *sysfs_path,
92 uint32_t *host)
93 {
94 g_autoptr(DIR) sysdir = NULL;
95 struct dirent *dirent = NULL;
96 int direrr;
97
98 VIR_DEBUG("Finding host number from '%s'", sysfs_path);
99
100 virWaitForDevices();
101
102 if (virDirOpen(&sysdir, sysfs_path) < 0)
103 return -1;
104
105 while ((direrr = virDirRead(sysdir, &dirent, sysfs_path)) > 0) {
106 if (STRPREFIX(dirent->d_name, "target")) {
107 if (sscanf(dirent->d_name, "target%u:", host) == 1) {
108 return 0;
109 } else {
110 virReportError(VIR_ERR_INTERNAL_ERROR,
111 _("Failed to parse target '%s'"), dirent->d_name);
112 return -1;
113 }
114 }
115 }
116
117 if (direrr == 0) {
118 virReportError(VIR_ERR_INTERNAL_ERROR,
119 _("Failed to get host number for iSCSI session "
120 "with path '%s'"), sysfs_path);
121 return -1;
122 }
123
124 return -1;
125 }
126
127 static int
virStorageBackendISCSIFindLUs(virStoragePoolObj * pool,const char * session)128 virStorageBackendISCSIFindLUs(virStoragePoolObj *pool,
129 const char *session)
130 {
131 uint32_t host;
132 g_autofree char *sysfs_path = NULL;
133
134 sysfs_path = g_strdup_printf("/sys/class/iscsi_session/session%s/device",
135 session);
136
137 if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0)
138 return -1;
139
140 if (virStorageBackendSCSIFindLUs(pool, host) < 0)
141 return -1;
142
143 return 0;
144 }
145
146
147 static char *
virStorageBackendISCSIFindPoolSources(const char * srcSpec,unsigned int flags)148 virStorageBackendISCSIFindPoolSources(const char *srcSpec,
149 unsigned int flags)
150 {
151 size_t ntargets = 0;
152 char **targets = NULL;
153 char *ret = NULL;
154 size_t i;
155 virStoragePoolSourceList list = {
156 .type = VIR_STORAGE_POOL_ISCSI,
157 .nsources = 0,
158 .sources = NULL
159 };
160 g_autofree char *portal = NULL;
161 g_autoptr(virStoragePoolSource) source = NULL;
162
163 virCheckFlags(0, NULL);
164
165 if (!srcSpec) {
166 virReportError(VIR_ERR_INVALID_ARG, "%s",
167 _("hostname must be specified for iscsi sources"));
168 return NULL;
169 }
170
171 if (!(source = virStoragePoolDefParseSourceString(srcSpec,
172 list.type)))
173 return NULL;
174
175 if (source->nhost != 1) {
176 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
177 _("Expected exactly 1 host for the storage pool"));
178 goto cleanup;
179 }
180
181 if (!(portal = virStorageBackendISCSIPortal(source)))
182 goto cleanup;
183
184 if (virISCSIScanTargets(portal,
185 source->initiator.iqn,
186 false,
187 &ntargets, &targets) < 0)
188 goto cleanup;
189
190 list.sources = g_new0(virStoragePoolSource, ntargets);
191
192 for (i = 0; i < ntargets; i++) {
193 list.sources[i].devices = g_new0(virStoragePoolSourceDevice, 1);
194 list.sources[i].hosts = g_new0(virStoragePoolSourceHost, 1);
195 list.sources[i].nhost = 1;
196 list.sources[i].hosts[0] = source->hosts[0];
197 list.sources[i].initiator = source->initiator;
198 list.sources[i].ndevice = 1;
199 list.sources[i].devices[0].path = targets[i];
200 list.nsources++;
201 }
202
203 if (!(ret = virStoragePoolSourceListFormat(&list)))
204 goto cleanup;
205
206 cleanup:
207 if (list.sources) {
208 for (i = 0; i < ntargets; i++) {
209 VIR_FREE(list.sources[i].hosts);
210 VIR_FREE(list.sources[i].devices);
211 }
212 VIR_FREE(list.sources);
213 }
214 for (i = 0; i < ntargets; i++)
215 VIR_FREE(targets[i]);
216 VIR_FREE(targets);
217 return ret;
218 }
219
220 static int
virStorageBackendISCSICheckPool(virStoragePoolObj * pool,bool * isActive)221 virStorageBackendISCSICheckPool(virStoragePoolObj *pool,
222 bool *isActive)
223 {
224 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
225 g_autofree char *session = NULL;
226
227 *isActive = false;
228
229 if (def->source.nhost != 1) {
230 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
231 _("Expected exactly 1 host for the storage pool"));
232 return -1;
233 }
234
235 if (def->source.hosts[0].name == NULL) {
236 virReportError(VIR_ERR_INTERNAL_ERROR,
237 "%s", _("missing source host"));
238 return -1;
239 }
240
241 if (def->source.ndevice != 1 ||
242 def->source.devices[0].path == NULL) {
243 virReportError(VIR_ERR_INTERNAL_ERROR,
244 "%s", _("missing source device"));
245 return -1;
246 }
247
248 if ((session = virStorageBackendISCSISession(pool, true)))
249 *isActive = true;
250 return 0;
251 }
252
253
254 static int
virStorageBackendISCSISetAuth(const char * portal,virStoragePoolSource * source)255 virStorageBackendISCSISetAuth(const char *portal,
256 virStoragePoolSource *source)
257 {
258 g_autofree unsigned char *secret_value = NULL;
259 size_t secret_size;
260 g_autofree char *secret_str = NULL;
261 virStorageAuthDef *authdef = source->auth;
262 int ret = -1;
263 virConnectPtr conn = NULL;
264 VIR_IDENTITY_AUTORESTORE virIdentity *oldident = NULL;
265
266 if (!authdef || authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE)
267 return 0;
268
269 VIR_DEBUG("username='%s' authType=%d seclookupdef.type=%d",
270 authdef->username, authdef->authType, authdef->seclookupdef.type);
271 if (authdef->authType != VIR_STORAGE_AUTH_TYPE_CHAP) {
272 virReportError(VIR_ERR_XML_ERROR, "%s",
273 _("iscsi pool only supports 'chap' auth type"));
274 return -1;
275 }
276
277 if (!(oldident = virIdentityElevateCurrent()))
278 return -1;
279
280 conn = virGetConnectSecret();
281 if (!conn)
282 return -1;
283
284 if (virSecretGetSecretString(conn, &authdef->seclookupdef,
285 VIR_SECRET_USAGE_TYPE_ISCSI,
286 &secret_value, &secret_size) < 0)
287 goto cleanup;
288
289 secret_str = g_new0(char, secret_size + 1);
290 memcpy(secret_str, secret_value, secret_size);
291 virSecureErase(secret_value, secret_size);
292 secret_str[secret_size] = '\0';
293
294 if (virISCSINodeUpdate(portal,
295 source->devices[0].path,
296 "node.session.auth.authmethod",
297 "CHAP") < 0 ||
298 virISCSINodeUpdate(portal,
299 source->devices[0].path,
300 "node.session.auth.username",
301 authdef->username) < 0 ||
302 virISCSINodeUpdate(portal,
303 source->devices[0].path,
304 "node.session.auth.password",
305 secret_str) < 0)
306 goto cleanup;
307
308 ret = 0;
309
310 cleanup:
311 virSecureErase(secret_str, secret_size);
312 virObjectUnref(conn);
313 return ret;
314 }
315
316 static int
virStorageBackendISCSIStartPool(virStoragePoolObj * pool)317 virStorageBackendISCSIStartPool(virStoragePoolObj *pool)
318 {
319 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
320 g_autofree char *portal = NULL;
321 g_autofree char *session = NULL;
322
323 if (def->source.nhost != 1) {
324 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
325 _("Expected exactly 1 host for the storage pool"));
326 return -1;
327 }
328
329 if (def->source.hosts[0].name == NULL) {
330 virReportError(VIR_ERR_INTERNAL_ERROR,
331 "%s", _("missing source host"));
332 return -1;
333 }
334
335 if (def->source.ndevice != 1 ||
336 def->source.devices[0].path == NULL) {
337 virReportError(VIR_ERR_INTERNAL_ERROR,
338 "%s", _("missing source device"));
339 return -1;
340 }
341
342 if ((session = virStorageBackendISCSISession(pool, true)) == NULL) {
343 if ((portal = virStorageBackendISCSIPortal(&def->source)) == NULL)
344 return -1;
345
346 /* Create a static node record for the IQN target. Must be done
347 * in order for login to the target */
348 if (virISCSINodeNew(portal, def->source.devices[0].path) < 0)
349 return -1;
350
351 if (virStorageBackendISCSISetAuth(portal, &def->source) < 0)
352 return -1;
353
354 if (virISCSIConnectionLogin(portal,
355 def->source.initiator.iqn,
356 def->source.devices[0].path) < 0)
357 return -1;
358 }
359 return 0;
360 }
361
362 static int
virStorageBackendISCSIRefreshPool(virStoragePoolObj * pool)363 virStorageBackendISCSIRefreshPool(virStoragePoolObj *pool)
364 {
365 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
366 g_autofree char *session = NULL;
367
368 def->allocation = def->capacity = def->available = 0;
369
370 if ((session = virStorageBackendISCSISession(pool, false)) == NULL)
371 return -1;
372 if (virISCSIRescanLUNs(session) < 0)
373 return -1;
374 if (virStorageBackendISCSIFindLUs(pool, session) < 0)
375 return -1;
376
377 return 0;
378 }
379
380
381 static int
virStorageBackendISCSIStopPool(virStoragePoolObj * pool)382 virStorageBackendISCSIStopPool(virStoragePoolObj *pool)
383 {
384 virStoragePoolDef *def = virStoragePoolObjGetDef(pool);
385 g_autofree char *portal = NULL;
386 g_autofree char *session = NULL;
387
388 if ((session = virStorageBackendISCSISession(pool, true)) == NULL)
389 return 0;
390
391 if ((portal = virStorageBackendISCSIPortal(&def->source)) == NULL)
392 return -1;
393
394 if (virISCSIConnectionLogout(portal,
395 def->source.initiator.iqn,
396 def->source.devices[0].path) < 0)
397 return -1;
398
399 return 0;
400 }
401
402 virStorageBackend virStorageBackendISCSI = {
403 .type = VIR_STORAGE_POOL_ISCSI,
404
405 .checkPool = virStorageBackendISCSICheckPool,
406 .startPool = virStorageBackendISCSIStartPool,
407 .refreshPool = virStorageBackendISCSIRefreshPool,
408 .stopPool = virStorageBackendISCSIStopPool,
409 .findPoolSources = virStorageBackendISCSIFindPoolSources,
410 .uploadVol = virStorageBackendVolUploadLocal,
411 .downloadVol = virStorageBackendVolDownloadLocal,
412 .wipeVol = virStorageBackendVolWipeLocal,
413 };
414
415
416 int
virStorageBackendISCSIRegister(void)417 virStorageBackendISCSIRegister(void)
418 {
419 return virStorageBackendRegister(&virStorageBackendISCSI);
420 }
421