/* * node_device_util.c: helper functions for the node device driver * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include "internal.h" #include "node_device_util.h" #include "virlog.h" #include "virscsihost.h" #include "virstring.h" #include "virvhba.h" #include "viralloc.h" #include "virutil.h" #define VIR_FROM_THIS VIR_FROM_NODEDEV VIR_LOG_INIT("node_device.node_device_util"); /* virNodeDeviceGetParentName * @conn: Connection pointer * @nodedev_name: Node device to lookup * * Lookup the node device by name and return the parent name * * Returns parent name on success, caller is responsible for freeing; * otherwise, returns NULL on failure */ char * virNodeDeviceGetParentName(virConnectPtr conn, const char *nodedev_name) { virNodeDevicePtr device = NULL; char *parent; if (!(device = virNodeDeviceLookupByName(conn, nodedev_name))) { virReportError(VIR_ERR_XML_ERROR, _("Cannot find '%s' in node device database"), nodedev_name); return NULL; } parent = g_strdup(virNodeDeviceGetParent(device)); virObjectUnref(device); return parent; } /** * @fchost: Pointer to vHBA adapter * * Create a vHBA for Storage. This code accomplishes this via searching * through the sysfs for scsi_host/fc_host in order to first ensure some * vHBA doesn't already exist for the requested wwnn/wwpn (e.g. an unmanaged * vHBA) and to search for the parent vport capable scsi_host by name, * wwnn/wwpn, or fabric_wwn (if provided). If no parent is provided, then * a vport capable scsi_host will be selected. * * Returns vHBA name on success, NULL on failure with an error message set */ char * virNodeDeviceCreateVport(virStorageAdapterFCHost *fchost) { unsigned int parent_host; char *name = NULL; char *parent_hoststr = NULL; bool skip_capable_check = false; VIR_DEBUG("parent='%s', wwnn='%s' wwpn='%s'", NULLSTR(fchost->parent), fchost->wwnn, fchost->wwpn); if (fchost->parent) { parent_hoststr = g_strdup(fchost->parent); } else if (fchost->parent_wwnn && fchost->parent_wwpn) { if (!(parent_hoststr = virVHBAGetHostByWWN(NULL, fchost->parent_wwnn, fchost->parent_wwpn))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("cannot find parent using provided wwnn/wwpn")); goto cleanup; } } else if (fchost->parent_fabric_wwn) { if (!(parent_hoststr = virVHBAGetHostByFabricWWN(NULL, fchost->parent_fabric_wwn))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("cannot find parent using provided fabric_wwn")); goto cleanup; } } else { if (!(parent_hoststr = virVHBAFindVportHost(NULL))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'parent' for vHBA not specified, and " "cannot find one on this host")); goto cleanup; } skip_capable_check = true; } if (virSCSIHostGetNumber(parent_hoststr, &parent_host) < 0) goto cleanup; /* NOTE: * We do not save the parent_hoststr in fchost->parent since * we could be writing out the 'def' to the saved XML config. * If we wrote out the name in the XML, then future starts would * always use the same parent rather than finding the "best available" * parent. Besides we have a way to determine the parent based on * the 'name' field. */ if (!skip_capable_check && !virVHBAPathExists(NULL, parent_host)) { virReportError(VIR_ERR_XML_ERROR, _("parent '%s' specified for vHBA does not exist"), parent_hoststr); goto cleanup; } if (virVHBAManageVport(parent_host, fchost->wwpn, fchost->wwnn, VPORT_CREATE) < 0) goto cleanup; /* Let's ensure the device was created */ virWaitForDevices(); if (!(name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) { ignore_value(virVHBAManageVport(parent_host, fchost->wwpn, fchost->wwnn, VPORT_DELETE)); goto cleanup; } cleanup: VIR_FREE(parent_hoststr); return name; } /** * @conn: Connection pointer * @fchost: Pointer to vHBA adapter * * As long as the vHBA is being managed, search for the scsi_host via the * provided wwnn/wwpn and then find the corresponding parent scsi_host in * order to send the delete request. * * Returns 0 on success, -1 on failure */ int virNodeDeviceDeleteVport(virConnectPtr conn, virStorageAdapterFCHost *fchost) { char *name = NULL; char *scsi_host_name = NULL; unsigned int parent_host; char *vhba_parent = NULL; int ret = -1; VIR_DEBUG("conn=%p parent='%s', managed='%d' wwnn='%s' wwpn='%s'", conn, NULLSTR(fchost->parent), fchost->managed, fchost->wwnn, fchost->wwpn); /* If we're not managing the deletion of the vHBA, then just return */ if (fchost->managed != VIR_TRISTATE_BOOL_YES) return 0; /* Find our vHBA by searching the fc_host sysfs tree for our wwnn/wwpn */ if (!(name = virVHBAGetHostByWWN(NULL, fchost->wwnn, fchost->wwpn))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to find fc_host for wwnn='%s' and wwpn='%s'"), fchost->wwnn, fchost->wwpn); goto cleanup; } scsi_host_name = g_strdup_printf("scsi_%s", name); /* If at startup time we provided a parent, then use that to * get the parent_host value; otherwise, we have to determine * the parent scsi_host which we did not save at startup time */ if (fchost->parent) { /* Someone provided a parent string at startup time that * was the same as the scsi_host - meaning we have a pool * backed to an HBA, so there won't be a vHBA to delete */ if (STREQ(scsi_host_name, fchost->parent)) { ret = 0; goto cleanup; } if (virSCSIHostGetNumber(fchost->parent, &parent_host) < 0) goto cleanup; } else { if (!(vhba_parent = virNodeDeviceGetParentName(conn, scsi_host_name))) goto cleanup; /* If the parent is not a scsi_host, then this is a pool backed * directly to an HBA and there's no vHBA to remove - so we're done */ if (!STRPREFIX(vhba_parent, "scsi_host")) { ret = 0; goto cleanup; } if (virSCSIHostGetNumber(vhba_parent, &parent_host) < 0) goto cleanup; } if (virVHBAManageVport(parent_host, fchost->wwpn, fchost->wwnn, VPORT_DELETE) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(name); VIR_FREE(vhba_parent); VIR_FREE(scsi_host_name); return ret; }