From 09a62b8389c8bf67f4bdd5e938d8b95e99d78e99 Mon Sep 17 00:00:00 2001 From: punith-cloudbyte Date: Thu, 30 Oct 2014 15:28:47 -0600 Subject: [PATCH] new managed nfs storage adapter Signed-off-by: Mike Tutkowski --- api/src/com/cloud/agent/api/to/DiskTO.java | 1 + api/src/com/cloud/storage/Storage.java | 3 +- .../kvm/storage/KVMStoragePoolManager.java | 1 + .../kvm/storage/LibvirtStoragePool.java | 5 + .../kvm/storage/ManagedNfsStorageAdaptor.java | 330 ++++++++++++++++++ 5 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java diff --git a/api/src/com/cloud/agent/api/to/DiskTO.java b/api/src/com/cloud/agent/api/to/DiskTO.java index 9e4dfd9e7cb..f982844486a 100644 --- a/api/src/com/cloud/agent/api/to/DiskTO.java +++ b/api/src/com/cloud/agent/api/to/DiskTO.java @@ -35,6 +35,7 @@ public class DiskTO { public static final String MOUNT_POINT = "mountpoint"; public static final String PROTOCOL_TYPE = "protocoltype"; public static final String PATH = "path"; + public static final String UUID = "uuid"; private DataTO data; private Long diskSeq; diff --git a/api/src/com/cloud/storage/Storage.java b/api/src/com/cloud/storage/Storage.java index 4d267eb7456..25ae5ca4dc9 100755 --- a/api/src/com/cloud/storage/Storage.java +++ b/api/src/com/cloud/storage/Storage.java @@ -131,7 +131,8 @@ public class Storage { EXT(false), // XenServer local EXT SR OCFS2(true), SMB(true), - Gluster(true); + Gluster(true), + ManagedNFS(true); boolean shared; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 583db0f4a36..eaf3ba654fa 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -96,6 +96,7 @@ public class KVMStoragePoolManager { // add other storage adaptors here // this._storageMapper.put("newadaptor", new NewStorageAdaptor(storagelayer)); this._storageMapper.put(StoragePoolType.Iscsi.toString(), new IscsiAdmStorageAdaptor()); + this._storageMapper.put(StoragePoolType.ManagedNFS.toString(), new ManagedNfsStorageAdaptor(storagelayer)); } public boolean connectPhysicalDisk(StoragePoolType type, String poolUuid, String volPath, Map details) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 1a756cb0d79..49a83010a28 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -256,6 +256,11 @@ public class LibvirtStoragePool implements KVMStoragePool { return this._pool; } + public void setPool(StoragePool pool) { + this._pool = pool; + } + + @Override public boolean delete() { try { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java new file mode 100644 index 00000000000..af2e6f6e125 --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/ManagedNfsStorageAdaptor.java @@ -0,0 +1,330 @@ +// 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. +package com.cloud.hypervisor.kvm.storage; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.LibvirtException; +import org.libvirt.StoragePool; +import org.libvirt.StoragePoolInfo; +import org.libvirt.StorageVol; + +import com.cloud.agent.api.to.DiskTO; +import com.cloud.hypervisor.kvm.resource.LibvirtConnection; +import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolDef; +import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolDef.poolType; +import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeDef; +import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeXMLParser; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.ProvisioningType; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.StorageLayer; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class ManagedNfsStorageAdaptor implements StorageAdaptor { + private static final Logger s_logger = Logger.getLogger(ManagedNfsStorageAdaptor.class); + private String _mountPoint = "/mnt"; + private StorageLayer _storageLayer; + + private static final Map MapStorageUuidToStoragePool = new HashMap(); + + public ManagedNfsStorageAdaptor(StorageLayer storagelayer) { + _storageLayer = storagelayer; + } + + @Override + public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType) { + + LibvirtStoragePool storagePool = new LibvirtStoragePool(uuid, path, StoragePoolType.ManagedNFS, this, null); + storagePool.setSourceHost(host); + storagePool.setSourcePort(port); + MapStorageUuidToStoragePool.put(uuid, storagePool); + + return storagePool; + } + + @Override + public KVMStoragePool getStoragePool(String uuid) { + return getStoragePool(uuid, false); + } + + @Override + public KVMStoragePool getStoragePool(String uuid, boolean refreshInfo) { + return MapStorageUuidToStoragePool.get(uuid); + } + + @Override + public boolean deleteStoragePool(String uuid) { + return MapStorageUuidToStoragePool.remove(uuid) != null; + } + + @Override + public boolean deleteStoragePool(KVMStoragePool pool) { + return deleteStoragePool(pool.getUuid()); + } + + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, long size) { + throw new UnsupportedOperationException("Creating a physical disk is not supported."); + } + + /* + * creates a nfs storage pool using libvirt + */ + @Override + public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map details) { + + StoragePool sp = null; + Connect conn = null; + String targetPath = null; + LibvirtStoragePoolDef spd = null; + try { + conn = LibvirtConnection.getConnection(); + + targetPath = "/mnt" + volumeUuid; + spd = new LibvirtStoragePoolDef(poolType.NETFS, volumeUuid, details.get(DiskTO.UUID), pool.getSourceHost(), details.get(DiskTO.MOUNT_POINT), targetPath); + _storageLayer.mkdir(targetPath); + + s_logger.debug(spd.toString()); + sp = conn.storagePoolCreateXML(spd.toString(), 0); + + if (sp == null) { + throw new CloudRuntimeException("Failed to create storage pool:" + volumeUuid); + } + + try { + if (sp.isActive() == 0) { + // s_logger.debug("attempting to activate pool " + name); + sp.create(0); + } + // now add the storage pool + LibvirtStoragePool storagePool = (LibvirtStoragePool) getStoragePool(pool.getUuid()); + storagePool.setPool(sp); + + return true; + } catch (LibvirtException e) { + String error = e.toString(); + if (error.contains("Storage source conflict")) { + throw new CloudRuntimeException("A pool matching this location already exists in libvirt, " + " but has a different UUID/Name. Cannot create new pool without first " + + " removing it. Check for inactive pools via 'virsh pool-list --all'. " + error); + } else { + throw new CloudRuntimeException(error); + } + } + } catch (LibvirtException e) { + s_logger.error(e.toString()); + // if error is that pool is mounted, try to handle it + if (e.toString().contains("already mounted")) { + s_logger.error("Attempting to unmount old mount libvirt is unaware of at " + targetPath); + String result = Script.runSimpleBashScript("umount -l " + targetPath); + if (result == null) { + s_logger.error("Succeeded in unmounting " + targetPath); + try { + sp = conn.storagePoolCreateXML(spd.toString(), 0); + s_logger.error("Succeeded in redefining storage"); + return true; + } catch (LibvirtException l) { + s_logger.error("Target was already mounted, unmounted it but failed to redefine storage:" + l); + } + } else { + s_logger.error("Failed in unmounting and redefining storage"); + } + } else { + s_logger.error("Internal error occurred when attempting to mount:" + e.getMessage()); + // stacktrace for agent.log + e.printStackTrace(); + throw new CloudRuntimeException(e.toString()); + } + return false; + } + + } + + /* + * creates a disk based on the created nfs storage pool using libvirt + */ + @Override + public KVMPhysicalDisk getPhysicalDisk(String volumeUuid, KVMStoragePool pool) { + // now create the volume upon the given storage pool in kvm + Connect conn; + StoragePool virtPool = null; + try { + conn = LibvirtConnection.getConnection(); + virtPool = conn.storagePoolLookupByName("/" + volumeUuid); + } catch (LibvirtException e1) { + throw new CloudRuntimeException(e1.toString()); + } + + LibvirtStorageVolumeDef.volFormat libvirtformat = null; + String volPath = null; + String volName = null; + long volAllocation = 0; + long volCapacity = 0; + // check whether the volume is present on the given pool + StorageVol vol = getVolume(virtPool, volumeUuid); + try { + if (vol == null) { + + libvirtformat = LibvirtStorageVolumeDef.volFormat.QCOW2; + + StoragePoolInfo poolinfo = virtPool.getInfo(); + volCapacity = poolinfo.available; + + LibvirtStorageVolumeDef volDef = new LibvirtStorageVolumeDef(volumeUuid, volCapacity, libvirtformat, null, null); + s_logger.debug(volDef.toString()); + + vol = virtPool.storageVolCreateXML(volDef.toString(), 0); + volPath = vol.getPath(); + volName = vol.getName(); + volAllocation = vol.getInfo().allocation; + volCapacity = vol.getInfo().capacity; + + } + + KVMPhysicalDisk disk = new KVMPhysicalDisk(vol.getPath(), volumeUuid, pool); + disk.setFormat(PhysicalDiskFormat.QCOW2); + disk.setSize(vol.getInfo().allocation); + disk.setVirtualSize(vol.getInfo().capacity); + return disk; + + } catch (LibvirtException e) { + throw new CloudRuntimeException(e.toString()); + } + + } + + public LibvirtStorageVolumeDef getStorageVolumeDef(Connect conn, StorageVol vol) throws LibvirtException { + String volDefXML = vol.getXMLDesc(0); + LibvirtStorageVolumeXMLParser parser = new LibvirtStorageVolumeXMLParser(); + return parser.parseStorageVolumeXML(volDefXML); + } + + public StorageVol getVolume(StoragePool pool, String volName) { + StorageVol vol = null; + + try { + vol = pool.storageVolLookupByName(volName); + } catch (LibvirtException e) { + s_logger.debug("Can't find volume: " + e.toString()); + } + if (vol == null) { + try { + refreshPool(pool); + } catch (LibvirtException e) { + s_logger.debug("failed to refresh pool: " + e.toString()); + } + s_logger.debug("no volume is present on the pool, creating a new one"); + } + return vol; + } + + private void refreshPool(StoragePool pool) throws LibvirtException { + pool.refresh(0); + return; + } + + /* + * disconnect the disk by destroying the sp pointer + */ + public boolean disconnectPhysicalDisk(KVMStoragePool pool, String mountpoint) throws LibvirtException { + + LibvirtStoragePool libvirtPool = (LibvirtStoragePool) pool; + StoragePool sp = libvirtPool.getPool(); + // destroy the pool + sp.destroy(); + + return true; + } + + @Override + public boolean disconnectPhysicalDisk(String volumeUuid, KVMStoragePool pool) { + try { + return disconnectPhysicalDisk(pool, volumeUuid); + } catch (LibvirtException e) { + throw new CloudRuntimeException(e.getMessage()); + } + } + + @Override + public boolean disconnectPhysicalDiskByPath(String localPath) { + return false; + } + + public boolean deletePhysicalDisk(String volumeUuid, KVMStoragePool pool) { + throw new UnsupportedOperationException("Deleting a physical disk is not supported."); + } + + @Override + public List listPhysicalDisks(String storagePoolUuid, KVMStoragePool pool) { + throw new UnsupportedOperationException("Listing disks is not supported for this configuration."); + } + + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + throw new UnsupportedOperationException("Creating a disk from a template is not yet supported for this configuration."); + } + + @Override + public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { + throw new UnsupportedOperationException("Creating a template from a disk is not yet supported for this configuration."); + } + + @Override + public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) { + throw new UnsupportedOperationException("Copying a disk is not supported in this configuration."); + } + + @Override + public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool) { + throw new UnsupportedOperationException("Creating a disk from a snapshot is not supported in this configuration."); + } + + @Override + public boolean refresh(KVMStoragePool pool) { + return true; + } + + @Override + public boolean createFolder(String uuid, String path) { + String mountPoint = _mountPoint + File.separator + uuid; + File f = new File(mountPoint + File.separator + path); + if (!f.exists()) { + f.mkdirs(); + } + return true; + } + + @Override + public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, ProvisioningType provisioningType, long size) { + return null; + } + + @Override + public boolean deletePhysicalDisk(String uuid, KVMStoragePool pool, ImageFormat format) { + return false; + } + + @Override + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { + return null; + } +}