blob: b23dd9a179042fda2c0e6c137a7d3472d69fdd07 [file] [log] [blame]
// 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;
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<String, KVMStoragePool> MapStorageUuidToStoragePool = new HashMap<String, KVMStoragePool>();
public ManagedNfsStorageAdaptor(StorageLayer storagelayer) {
_storageLayer = storagelayer;
}
@Override
public KVMStoragePool createStoragePool(String uuid, String host, int port, String path, String userInfo, StoragePoolType storagePoolType, Map<String, String> details) {
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<String, String> details) {
StoragePool sp = null;
Connect conn = null;
String targetPath = null;
LibvirtStoragePoolDef spd = null;
try {
conn = LibvirtConnection.getConnection();
if (conn == null) {
throw new CloudRuntimeException("Failed to create Libvrt Connection");
}
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 {
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.VolumeFormat libvirtformat = null;
long volCapacity = 0;
// check whether the volume is present on the given pool
StorageVol vol = getVolume(virtPool, volumeUuid);
try {
if (vol == null) {
libvirtformat = LibvirtStorageVolumeDef.VolumeFormat.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);
}
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 disconnectPhysicalDisk(Map<String, String> volumeToDisconnect) {
return false;
}
@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<KVMPhysicalDisk> 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) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase, ProvisioningType provisioningType) {
throw new UnsupportedOperationException("Copying a disk is not supported in this configuration.");
}
@Override
public boolean refresh(KVMStoragePool pool) {
return true;
}
@Override
public boolean createFolder(String uuid, String path) {
return createFolder(uuid, path, null);
}
@Override
public boolean createFolder(String uuid, String path, String localPath) {
String mountPoint = _mountPoint + File.separator + uuid;
File f = new File(mountPoint + File.separator + path);
if (!f.exists()) {
return f.mkdirs();
}
return true;
}
@Override
public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout, byte[] passphrase) {
return null;
}
@Override
public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFilePath, String destTemplatePath, KVMStoragePool destPool, Storage.ImageFormat format, int timeout) {
return null;
}
@Override
public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, ProvisioningType provisioningType, long size, byte[] passphrase) {
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, byte[] passphrase) {
return null;
}
}