From 29b4cde04a8d60f83cf35f44b2c9f62697fd0ec0 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Fri, 20 Sep 2013 10:21:48 -0700 Subject: [PATCH 01/33] CS-18283: 2.2.x to 4.2 upgrade - corrected the Service Provider name for the Network offeirng that can be used in VPC (from VirtualRouter to VpcVirtualRouter) --- .../cloudstack/engine/orchestration/NetworkOrchestrator.java | 2 +- setup/db/db/schema-410to420.sql | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index d8c3b8e5132..53f64fd77e9 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -390,7 +390,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Map> defaultVPCOffProviders = new HashMap>(); defaultProviders.clear(); - defaultProviders.add(Network.Provider.VirtualRouter); + defaultProviders.add(Network.Provider.VPCVirtualRouter); defaultVPCOffProviders.put(Service.Dhcp, defaultProviders); defaultVPCOffProviders.put(Service.Dns, defaultProviders); defaultVPCOffProviders.put(Service.UserData, defaultProviders); diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index e7d9f4423be..522ccc4a272 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -2371,3 +2371,7 @@ CREATE VIEW `cloud`.`data_center_view` AS `cloud`.`dedicated_resources` ON data_center.id = dedicated_resources.data_center_id left join `cloud`.`affinity_group` ON dedicated_resources.affinity_group_id = affinity_group.id; + + + +UPDATE `cloud`.`ntwk_offering_service_map` SET Provider='VpcVirtualRouter' WHERE network_offering_id IN (SELECT id from `cloud`.`network_offerings` WHERE name IN ('DefaultIsolatedNetworkOfferingForVpcNetworks', 'DefaultIsolatedNetworkOfferingForVpcNetworksNoLB')); From 3e416da0bc97e7faff06725a58d81eb9429c325b Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Wed, 25 Sep 2013 10:25:24 +0200 Subject: [PATCH 02/33] kvm: Use libvirt-java 0.5.1 This version is Java 6 compatible and makes CloudStack Java 6 compatible again --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8778f11402..1c85bc43d30 100644 --- a/pom.xml +++ b/pom.xml @@ -81,7 +81,7 @@ 0.9.8 0.10 build/replace.properties - 0.5.0 + 0.5.1 0.1.3 target 1.0.10 From 3dc4284a34a1c79970b30288c245a26e8425e811 Mon Sep 17 00:00:00 2001 From: Wei Zhou Date: Wed, 25 Sep 2013 11:08:57 +0200 Subject: [PATCH 03/33] add missing jna-4.0.0.jar to cloudstack-agent library by changing scope from provided to default runtime --- plugins/hypervisors/kvm/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml index 4c0ec982bdf..024cafe834c 100644 --- a/plugins/hypervisors/kvm/pom.xml +++ b/plugins/hypervisors/kvm/pom.xml @@ -51,7 +51,6 @@ net.java.dev.jna jna - provided ${cs.jna.version} From 9fb0a1a61992f93e2105d9a99d507d48a108b0c7 Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Wed, 25 Sep 2013 14:34:14 +0200 Subject: [PATCH 04/33] Don't check implementation version of Object for DatabaseUpgradeChecker --- .../schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java index f001bf71810..c107fa021ff 100755 --- a/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -383,9 +383,6 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { try { String dbVersion = _dao.getCurrentVersion(); String currentVersion = this.getClass().getPackage().getImplementationVersion(); - if (currentVersion == null) { - currentVersion = this.getClass().getSuperclass().getPackage().getImplementationVersion(); - } if (currentVersion == null) return; From 77bec4f2adf230a1b0b384922119892547ccc88a Mon Sep 17 00:00:00 2001 From: Abhinandan Prateek Date: Wed, 25 Sep 2013 21:09:24 +0530 Subject: [PATCH 05/33] Cloudstack-2997: the maxconn setting is masked by wrong conditions --- .../src/com/cloud/configuration/ConfigurationManagerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 3c77958fe30..f23d57d84bf 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3770,7 +3770,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } validateLoadBalancerServiceCapabilities(lbServiceCapabilityMap); - if (!serviceProviderMap.containsKey(Service.Lb) && lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { + if (lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { maxconn = cmd.getMaxconnections(); if (maxconn == null) { maxconn=Integer.parseInt(_configDao.getValue(Config.NetworkLBHaproxyMaxConn.key())); From b3ac12e171713a11753157d0f207e11123fc6116 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 20 Aug 2013 15:33:05 -0700 Subject: [PATCH 06/33] delete snapshot_store_ref if the snapshot is in error state --- .../datastore/db/SnapshotDataStoreDao.java | 1 + .../image/db/SnapshotDataStoreDaoImpl.java | 13 ++++++++ .../kvm/storage/LibvirtStorageAdaptor.java | 3 +- .../com/cloud/storage/StorageManagerImpl.java | 4 +++ tools/marvin/marvin/deployAndRun.py | 32 +++++++++++++++---- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index f9037150c93..e350a763449 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -40,4 +40,5 @@ StateDao listDestroyed(long storeId); + List findBySnapshotId(long snapshotId); } diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index d8e6abcf110..1935a881ded 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.naming.ConfigurationException; +import com.cloud.utils.db.SearchCriteria2; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; @@ -51,6 +52,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase cacheSearch; private SearchBuilder snapshotSearch; private SearchBuilder storeSnapshotSearch; + private SearchBuilder snapshotIdSearch; private String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? " + " and store_role = ? and volume_id = ? and state = 'Ready'" + " order by created DESC " + @@ -101,6 +103,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase findBySnapshotId(long snapshotId) { + SearchCriteria sc = snapshotIdSearch.create(); + sc.setParameters("snapshot_id", snapshotId); + return listBy(sc); + } + @Override public List listDestroyed(long id) { SearchCriteria sc = destroyedSearch.create(); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 5760725cbe8..1be94d91125 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -397,7 +397,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { return pool; } catch (LibvirtException e) { - throw new CloudRuntimeException(e.toString()); + s_logger.debug("can't get storage pool",e); + throw new CloudRuntimeException(e.toString(), e); } } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 8417066cffb..96ffe35756d 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1042,6 +1042,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C List snapshots = _snapshotDao.listAllByStatus(Snapshot.State.Error); for (SnapshotVO snapshotVO : snapshots) { try { + List storeRefs = _snapshotStoreDao.findBySnapshotId(snapshotVO.getId()); + for(SnapshotDataStoreVO ref : storeRefs) { + _snapshotStoreDao.expunge(ref.getId()); + } _snapshotDao.expunge(snapshotVO.getId()); } catch (Exception e) { s_logger.warn("Unable to destroy " + snapshotVO.getId(), e); diff --git a/tools/marvin/marvin/deployAndRun.py b/tools/marvin/marvin/deployAndRun.py index 78c161789a7..d162fb39685 100644 --- a/tools/marvin/marvin/deployAndRun.py +++ b/tools/marvin/marvin/deployAndRun.py @@ -17,6 +17,7 @@ import deployDataCenter import TestCaseExecuteEngine +import sys from argparse import ArgumentParser if __name__ == "__main__": @@ -37,6 +38,8 @@ if __name__ == "__main__": parser.add_argument("-l", "--load", dest="load", action="store_true", help="only load config, do not deploy,\ it will only run testcase") + parser.add_argument("-n", "--num", dest="number", + help="how many times you want run the test case") options = parser.parse_args() @@ -52,23 +55,38 @@ if __name__ == "__main__": deploy.loadCfg() else: deploy.deploy() + iterates = 1 + if options.number is not None: + if options.number == "loop": + iterates = sys.maxint + else: + try: + iterates = int(options.number) + except: + iterates = 1 if options.testCaseFolder is None: if options.module is None: parser.print_usage() exit(1) else: - engine = \ - TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, + n = 0 + while(n < iterates): + engine = \ + TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, deploy.getCfg(), testCaseLogFile, testResultLogFile) - engine.loadTestsFromFile(options.module) - engine.run() + engine.loadTestsFromFile(options.module) + engine.run() + n = n + 1 else: - engine = TestCaseExecuteEngine.TestCaseExecuteEngine(deploy.testClient, + n = 0 + while(n Date: Wed, 21 Aug 2013 16:33:40 -0700 Subject: [PATCH 07/33] CLOUDSTACK-2729: another try to solve libvirt storage pool missing issue: if the storage pool is missing, then recreate it --- .../kvm/storage/KVMStoragePoolManager.java | 46 +++++++++++++++++-- .../kvm/storage/LibvirtStorageAdaptor.java | 39 ++-------------- 2 files changed, 44 insertions(+), 41 deletions(-) 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 31d6179e8d9..40f4b5b785f 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 @@ -33,9 +33,35 @@ import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; public class KVMStoragePoolManager { + private class StoragePoolInformation { + String name; + String host; + int port; + String path; + String userInfo; + boolean type; + StoragePoolType poolType; + + + public StoragePoolInformation(String name, + String host, + int port, + String path, + String userInfo, + StoragePoolType poolType, + boolean type) { + this.name = name; + this.host = host; + this.port = port; + this.path = path; + this.userInfo = userInfo; + this.type = type; + this.poolType = poolType; + } + } private StorageAdaptor _storageAdaptor; private KVMHAMonitor _haMonitor; - private final Map _storagePools = new ConcurrentHashMap(); + private final Map _storagePools = new ConcurrentHashMap(); private final Map _storageMapper = new HashMap(); private StorageAdaptor getStorageAdaptor(StoragePoolType type) { @@ -51,10 +77,10 @@ public class KVMStoragePoolManager { return adaptor; } - private void addStoragePool(String uuid) { + private void addStoragePool(String uuid, StoragePoolInformation pool) { synchronized (_storagePools) { if (!_storagePools.containsKey(uuid)) { - _storagePools.put(uuid, new Object()); + _storagePools.put(uuid, pool); } } } @@ -69,7 +95,16 @@ public class KVMStoragePoolManager { public KVMStoragePool getStoragePool(StoragePoolType type, String uuid) { StorageAdaptor adaptor = getStorageAdaptor(type); - return adaptor.getStoragePool(uuid); + KVMStoragePool pool = null; + try { + pool = adaptor.getStoragePool(uuid); + } catch(Exception e) { + StoragePoolInformation info = _storagePools.get(uuid); + if (info != null) { + pool = createStoragePool(info.name, info.host, info.port, info.path, info.userInfo, info.poolType, info.type); + } + } + return pool; } public KVMStoragePool getStoragePoolByURI(String uri) { @@ -119,7 +154,8 @@ public class KVMStoragePoolManager { PoolType.PrimaryStorage); _haMonitor.addStoragePool(nfspool); } - addStoragePool(pool.getUuid()); + StoragePoolInformation info = new StoragePoolInformation(name, host, port, path, userInfo, type, primaryStorage); + addStoragePool(pool.getUuid(), info); return pool; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 1be94d91125..53eb80a922b 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -1170,45 +1170,12 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { // However, we also need to fix the issues in CloudStack source code. // A file lock is used to prevent deleting a volume from a KVM storage pool when refresh it. private void refreshPool(StoragePool pool) throws LibvirtException { - Connect conn = LibvirtConnection.getConnection(); - LibvirtStoragePoolDef spd = getStoragePoolDef(conn, pool); - if ((! spd.getPoolType().equals(LibvirtStoragePoolDef.poolType.NETFS)) - && (! spd.getPoolType().equals(LibvirtStoragePoolDef.poolType.DIR))) { - pool.refresh(0); - return; - } - String lockFile = spd.getTargetPath() + File.separator + _lockfile; - s_logger.debug("Attempting to lock pool " + pool.getName() + " with file " + lockFile); - if (lock(lockFile, ACQUIRE_GLOBAL_FILELOCK_TIMEOUT_FOR_KVM)) { - try { - pool.refresh(0); - } finally { - s_logger.debug("Releasing the lock on pool " + pool.getName() + " with file " + lockFile); - unlock(lockFile); - } - } else { - throw new CloudRuntimeException("Can not get file lock to refresh the pool " + pool.getName()); - } + pool.refresh(0); + return; } private void deleteVol(LibvirtStoragePool pool, StorageVol vol) throws LibvirtException { - if ((! pool.getType().equals(StoragePoolType.NetworkFilesystem)) - && (! pool.getType().equals(StoragePoolType.Filesystem))) { - vol.delete(0); - return; - } - String lockFile = pool.getLocalPath() + File.separator + _lockfile; - s_logger.debug("Attempting to lock pool " + pool.getName() + " with file " + lockFile); - if (lock(lockFile, ACQUIRE_GLOBAL_FILELOCK_TIMEOUT_FOR_KVM)) { - try { - vol.delete(0); - } finally { - s_logger.debug("Releasing the lock on pool " + pool.getName() + " with file " + lockFile); - unlock(lockFile); - } - } else { - throw new CloudRuntimeException("Can not get file lock to delete the volume " + vol.getName()); - } + vol.delete(0); } private boolean lock(String path, int wait) { From 7b6aebc1029088a65710f38f730bcd3b92fb1ad3 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 15:58:46 -0700 Subject: [PATCH 08/33] add test Conflicts: engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java --- .../storage/test/ChildTestConfiguration.java | 6 --- .../test/FakeDriverTestConfiguration.java | 3 ++ .../test/MockStorageMotionStrategy.java | 24 +++++++++++- .../test/SnapshotTestWithFakeData.java | 39 ++++++++++++++++++- .../test/resource/fakeDriverTestContext.xml | 3 +- .../storage/snapshot/SnapshotManagerImpl.java | 4 -- 6 files changed, 66 insertions(+), 13 deletions(-) diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 0f97f311dcd..272411880f0 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -186,12 +186,6 @@ public class ChildTestConfiguration extends TestConfiguration { return Mockito.mock(VirtualMachineManager.class); } - - @Bean - public SnapshotManager snapshotMgr() { - return Mockito.mock(SnapshotManager.class); - } - @Bean public ResourceManager resourceMgr() { return Mockito.mock(ResourceManager.class); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java index 75eda90c864..943b73cb97e 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java @@ -18,10 +18,12 @@ */ package org.apache.cloudstack.storage.test; + import com.cloud.storage.snapshot.SnapshotScheduler; import com.cloud.storage.snapshot.SnapshotSchedulerImpl; import com.cloud.user.DomainManager; import com.cloud.utils.component.ComponentContext; + import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -48,6 +50,7 @@ public class FakeDriverTestConfiguration extends ChildTestConfiguration{ return strategy; } + @Bean public SnapshotScheduler SnapshotScheduler() { return Mockito.mock(SnapshotScheduler.class); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java index 52ccf410c8d..2a836892ebb 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java @@ -19,7 +19,11 @@ package org.apache.cloudstack.storage.test; import java.util.Map; +import java.util.UUID; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; +import com.cloud.storage.Storage; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; @@ -29,6 +33,9 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; public class MockStorageMotionStrategy implements DataMotionStrategy { @@ -45,7 +52,22 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { - CopyCommandResult result = new CopyCommandResult("something", null); + CopyCmdAnswer answer = null; + DataTO data = null; + if (destData.getType() == DataObjectType.SNAPSHOT) { + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(UUID.randomUUID().toString()); + data = newSnapshot; + } else if (destData.getType() == DataObjectType.TEMPLATE) { + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(UUID.randomUUID().toString()); + newTemplate.setFormat(Storage.ImageFormat.QCOW2); + newTemplate.setSize(10L); + newTemplate.setPhysicalSize(10L); + data = newTemplate; + } + answer = new CopyCmdAnswer(data); + CopyCommandResult result = new CopyCommandResult("something", answer); callback.complete(result); return null; } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java index c98f7056662..d5465056aa6 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java @@ -29,6 +29,7 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.DB; import junit.framework.Assert; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -61,10 +62,17 @@ import java.net.URISyntaxException; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/fakeDriverTestContext.xml" }) -public class SnapshotTestWithFakeData { +public class SnapshotTestWithFakeData { @Inject SnapshotService snapshotService; @Inject @@ -85,6 +93,7 @@ public class SnapshotTestWithFakeData { VolumeService volumeService; @Inject VolumeDataFactory volumeDataFactory; + VolumeInfo vol = null; FakePrimaryDataStoreDriver driver = new FakePrimaryDataStoreDriver(); @Before @@ -191,4 +200,32 @@ public class SnapshotTestWithFakeData { Assert.assertTrue(result == null); } + @Test + public void testConcurrentSnapshot() throws URISyntaxException, InterruptedException, ExecutionException { + DataStore store = createDataStore(); + FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); + dataStoreDriver.makeTakeSnapshotSucceed(true); + final VolumeInfo volumeInfo = createVolume(1L, store); + Assert.assertTrue(volumeInfo.getState() == Volume.State.Ready); + vol = volumeInfo; + ExecutorService pool = Executors.newFixedThreadPool(2); + boolean result = false; + Future future = null; + for(int i = 0; i < 1; i++) { + future = pool.submit(new Callable() { + @Override + public Boolean call() throws Exception { + boolean r = false; + try { + SnapshotInfo newSnapshot = volumeService.takeSnapshot(vol); + Assert.assertTrue(newSnapshot != null); + } catch (Exception e) { + r = false; + } + return true; + } + }); + } + future.get(); + } } diff --git a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml index 3abcf08090b..98209de67e9 100644 --- a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml +++ b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml @@ -48,7 +48,6 @@ - @@ -84,4 +83,6 @@ + + diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index a884b9542c8..6574b7f2447 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -140,8 +140,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject protected VMTemplateDao _templateDao; @Inject - protected HostDao _hostDao; - @Inject protected UserVmDao _vmDao; @Inject protected VolumeDao _volsDao; @@ -158,8 +156,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject protected PrimaryDataStoreDao _storagePoolDao; @Inject - protected EventDao _eventDao; - @Inject protected SnapshotPolicyDao _snapshotPolicyDao = null; @Inject protected SnapshotScheduleDao _snapshotScheduleDao; From b5f7e307a11e0fb7305b94f0e0795f89b7abbb7d Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:01:45 -0700 Subject: [PATCH 09/33] CLOUDSTACK-4456: CLOUDSTACK-4457: CLOUDSTACK-4459: harden kvm getvolume. It's possible that one volume created on other kvm host, won't show up on another host, try more times to refresh storage pool if volume won't shown up Conflicts: engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java --- .../test/FakeDriverTestConfiguration.java | 5 +- .../test/resource/fakeDriverTestContext.xml | 2 + .../datastore/PrimaryDataStoreImpl.java | 6 +++ .../resource/LibvirtComputingResource.java | 19 ++++--- .../kvm/storage/KVMStoragePoolManager.java | 33 ++++++++++++ .../kvm/storage/KVMStorageProcessor.java | 53 ++++++------------- 6 files changed, 70 insertions(+), 48 deletions(-) diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java index 943b73cb97e..5e3e8851066 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java @@ -24,6 +24,7 @@ import com.cloud.storage.snapshot.SnapshotSchedulerImpl; import com.cloud.user.DomainManager; import com.cloud.utils.component.ComponentContext; + import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -50,7 +51,6 @@ public class FakeDriverTestConfiguration extends ChildTestConfiguration{ return strategy; } - @Bean public SnapshotScheduler SnapshotScheduler() { return Mockito.mock(SnapshotScheduler.class); @@ -61,10 +61,11 @@ public class FakeDriverTestConfiguration extends ChildTestConfiguration{ return Mockito.mock(DomainManager.class); } + @Override @Bean public EndPointSelector selector() { return ComponentContext.inject(DefaultEndPointSelector.class); } - } + diff --git a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml index 98209de67e9..c3c8ef5a6bb 100644 --- a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml +++ b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml @@ -85,4 +85,6 @@ + + diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 7c8f49a9c9b..22ff2209b19 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -266,6 +266,12 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { } } else if (obj.getType() == DataObjectType.SNAPSHOT) { return objectInStoreMgr.create(obj, this); + } else if (obj.getType() == DataObjectType.VOLUME) { + VolumeVO vol = volumeDao.findById(obj.getId()); + if (vol != null) { + vol.setPoolId(this.getId()); + volumeDao.update(vol.getId(), vol); + } } return objectInStoreMgr.get(obj, this); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 74f02c0c639..6a7a56251a1 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3627,10 +3627,10 @@ ServerResource { physicalDisk = secondaryStorage.getPhysicalDisk(volName); } else if (volume.getType() != Volume.Type.ISO) { PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); - pool = _storagePoolMgr.getStoragePool( - store.getPoolType(), - store.getUuid()); - physicalDisk = pool.getPhysicalDisk(data.getPath()); + physicalDisk = _storagePoolMgr.getPhysicalDisk( store.getPoolType(), + store.getUuid(), + data.getPath()); + pool = physicalDisk.getPool(); } String volPath = null; @@ -3703,10 +3703,9 @@ ServerResource { if (volume.getType() == Volume.Type.ROOT) { DataTO data = volume.getData(); PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); - KVMStoragePool pool = _storagePoolMgr.getStoragePool( - store.getPoolType(), - store.getUuid()); - KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(data.getPath()); + KVMPhysicalDisk physicalDisk = _storagePoolMgr.getPhysicalDisk( store.getPoolType(), + store.getUuid(), + data.getPath()); FilesystemDef rootFs = new FilesystemDef(physicalDisk.getPath(), "/"); vm.getDevices().addDevice(rootFs); break; @@ -3748,6 +3747,10 @@ ServerResource { // need to umount secondary storage String path = disk.getDiskPath(); String poolUuid = null; + if (path.endsWith("systemvm.iso")) { + //Don't need to clean up system vm iso, as it's stored in local + return true; + } if (path != null) { String[] token = path.split("/"); if (token.length > 3) { 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 40f4b5b785f..71bbc15e37c 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 @@ -31,8 +31,11 @@ import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.log4j.Logger; public class KVMStoragePoolManager { + private static final Logger s_logger = Logger + .getLogger(KVMStoragePoolManager.class); private class StoragePoolInformation { String name; String host; @@ -133,6 +136,36 @@ public class KVMStoragePoolManager { return createStoragePool(uuid, sourceHost, 0, sourcePath, "", protocol, false); } + public KVMPhysicalDisk getPhysicalDisk(StoragePoolType type, String poolUuid, String volName) { + int cnt = 0; + int retries = 10; + KVMPhysicalDisk vol = null; + //harden get volume, try cnt times to get volume, in case volume is created on other host + String errMsg = ""; + while (cnt < retries) { + try { + KVMStoragePool pool = getStoragePool(type, poolUuid); + vol = pool.getPhysicalDisk(volName); + if (vol != null) { + break; + } + + Thread.sleep(10000); + } catch (Exception e) { + s_logger.debug("Failed to find volume:" + volName + " due to" + e.toString() + ", retry:" + cnt); + errMsg = e.toString(); + } + cnt++; + } + + if (vol == null) { + throw new CloudRuntimeException(errMsg); + } else { + return vol; + } + + } + public KVMStoragePool createStoragePool( String name, String host, int port, String path, String userInfo, StoragePoolType type) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 3cca4fd087b..5ed93aab496 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -289,8 +289,8 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { vol = templateToPrimaryDownload(templatePath, primaryPool); } else { - BaseVol = primaryPool.getPhysicalDisk(templatePath); - vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), primaryPool); + BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -394,28 +394,14 @@ public class KVMStorageProcessor implements StorageProcessor { String destVolumePath = destData.getPath(); String secondaryStorageUrl = nfsStore.getUrl(); KVMStoragePool secondaryStoragePool = null; - KVMStoragePool primaryPool = null; - try { - try { - primaryPool = storagePoolMgr.getStoragePool( - primaryStore.getPoolType(), - primaryStore.getUuid()); - } catch (CloudRuntimeException e) { - if (e.getMessage().contains("not found")) { - primaryPool = storagePoolMgr.createStoragePool(primaryStore.getUuid(), - primaryStore.getHost(), primaryStore.getPort(), - primaryStore.getPath(), null, - primaryStore.getPoolType()); - } else { - return new CopyCmdAnswer(e.getMessage()); - } - } + try { String volumeName = UUID.randomUUID().toString(); String destVolumeName = volumeName + "." + destFormat.getFileExtension(); - KVMPhysicalDisk volume = primaryPool.getPhysicalDisk(srcVolumePath); + KVMPhysicalDisk volume = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), srcVolumePath); volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); + secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( secondaryStorageUrl); secondaryStoragePool.createFolder(destVolumePath); @@ -459,18 +445,9 @@ public class KVMStorageProcessor implements StorageProcessor { secondaryStorage = storagePoolMgr.getStoragePoolByURI(nfsImageStore.getUrl()); - try { - primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - } catch (CloudRuntimeException e) { - if (e.getMessage().contains("not found")) { - primary = storagePoolMgr.createStoragePool(primaryStore.getUuid(), primaryStore.getHost(), - primaryStore.getPort(), primaryStore.getPath(), null, primaryStore.getPoolType()); - } else { - return new CopyCmdAnswer(e.getMessage()); - } - } + primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - KVMPhysicalDisk disk = primary.getPhysicalDisk(volume.getPath()); + KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), volume.getPath()); String tmpltPath = secondaryStorage.getLocalPath() + File.separator + templateFolder; this.storageLayer.mkdirs(tmpltPath); String templateName = UUID.randomUUID().toString(); @@ -661,9 +638,9 @@ public class KVMStorageProcessor implements StorageProcessor { snapshotRelPath = destSnapshot.getPath(); snapshotDestPath = ssPmountPath + File.separator + snapshotRelPath; - KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), - primaryStore.getUuid()); - KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(volumePath); + KVMPhysicalDisk snapshotDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), + primaryStore.getUuid(), volumePath); + KVMStoragePool primaryPool = snapshotDisk.getPool(); /** * RBD snapshots can't be copied using qemu-img, so we have to use @@ -798,6 +775,7 @@ public class KVMStorageProcessor implements StorageProcessor { } } + protected synchronized String attachOrDetachISO(Connect conn, String vmName, String isoPath, boolean isAttach) throws LibvirtException, URISyntaxException, InternalErrorException { String isoXml = null; @@ -967,9 +945,8 @@ public class KVMStorageProcessor implements StorageProcessor { String vmName = cmd.getVmName(); try { Connect conn = LibvirtConnection.getConnectionByVmName(vmName); - KVMStoragePool primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); + KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); - KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); attachOrDetachDisk(conn, true, vmName, phyDisk, disk.getDiskSeq().intValue()); return new AttachAnswer(disk); @@ -990,9 +967,8 @@ public class KVMStorageProcessor implements StorageProcessor { String vmName = cmd.getVmName(); try { Connect conn = LibvirtConnection.getConnectionByVmName(vmName); - KVMStoragePool primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); + KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); - KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); attachOrDetachDisk(conn, false, vmName, phyDisk, disk.getDiskSeq().intValue()); return new DettachAnswer(disk); @@ -1064,7 +1040,8 @@ public class KVMStorageProcessor implements StorageProcessor { KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(volume.getPath()); + KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), + primaryStore.getUuid(), volume.getPath()); if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && !primaryPool.isExternalSnapshot()) { String vmUuid = vm.getUUIDString(); Object[] args = new Object[] { snapshotName, vmUuid }; From a82b1798789eb05bd3653af1c46157eb16627be3 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:03:59 -0700 Subject: [PATCH 10/33] add table lock on snapshot, during taking snapshot Conflicts: engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java --- .../storage/test/ChildTestConfiguration.java | 4 +- .../test/MockStorageMotionStrategy.java | 17 ++ .../test/SnapshotTestWithFakeData.java | 170 +++++++++++++++--- .../test/resource/fakeDriverTestContext.xml | 1 + .../snapshot/XenserverSnapshotStrategy.java | 66 ++++--- .../image/db/SnapshotDataStoreDaoImpl.java | 6 +- .../storage/volume/VolumeServiceImpl.java | 6 +- .../storage/snapshot/SnapshotManagerImpl.java | 21 ++- 8 files changed, 230 insertions(+), 61 deletions(-) diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 272411880f0..d5eea85fcd2 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -18,6 +18,8 @@ package org.apache.cloudstack.storage.test; import java.io.IOException; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.dao.EventDaoImpl; import org.apache.cloudstack.acl.APIChecker; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.service.api.OrchestrationService; @@ -107,7 +109,7 @@ import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl; VMSnapshotDaoImpl.class, OCFS2ManagerImpl.class, ClusterDetailsDaoImpl.class, SecondaryStorageVmDaoImpl.class, ConsoleProxyDaoImpl.class, StoragePoolWorkDaoImpl.class, StorageCacheManagerImpl.class, UserDaoImpl.class, DataCenterDaoImpl.class, StoragePoolDetailsDaoImpl.class, DomainDaoImpl.class, DownloadMonitorImpl.class, - AccountDaoImpl.class }, includeFilters = { @Filter(value = Library.class, type = FilterType.CUSTOM) }, + AccountDaoImpl.class, ActionEventUtils.class, EventDaoImpl.class}, includeFilters = { @Filter(value = Library.class, type = FilterType.CUSTOM) }, useDefaultFilters = false) public class ChildTestConfiguration extends TestConfiguration { diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java index 2a836892ebb..6c0bd556480 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java @@ -28,23 +28,30 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.snapshot.SnapshotObject; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; public class MockStorageMotionStrategy implements DataMotionStrategy { + boolean success = true; @Override public boolean canHandle(DataObject srcData, DataObject destData) { // TODO Auto-generated method stub return true; } + public void makeBackupSnapshotSucceed(boolean success) { + this.success = success; + } + @Override public boolean canHandle(Map volumeMap, Host srcHost, Host destHost) { return true; @@ -54,9 +61,19 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCmdAnswer answer = null; DataTO data = null; + if (!success) { + CopyCommandResult result = new CopyCommandResult(null, null); + result.setResult("Failed"); + callback.complete(result); + } if (destData.getType() == DataObjectType.SNAPSHOT) { + SnapshotInfo srcSnapshot = (SnapshotInfo)srcData; + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); newSnapshot.setPath(UUID.randomUUID().toString()); + if (srcSnapshot.getParent() != null) { + newSnapshot.setParentSnapshotPath(srcSnapshot.getParent().getPath()); + } data = newSnapshot; } else if (destData.getType() == DataObjectType.TEMPLATE) { TemplateObjectTO newTemplate = new TemplateObjectTO(); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java index d5465056aa6..2aaabdaf430 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java @@ -18,18 +18,37 @@ */ package org.apache.cloudstack.storage.test; +import com.cloud.cluster.LockMasterListener; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.HostPodDao; import com.cloud.hypervisor.Hypervisor; +import com.cloud.org.Cluster; +import com.cloud.org.Managed; +import com.cloud.storage.CreateSnapshotPayload; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.SnapshotPolicyDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.User; +import com.cloud.utils.DateUtil; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Merovingian2; import junit.framework.Assert; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -45,10 +64,14 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.storage.datastore.PrimaryDataStore; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.volume.VolumeObject; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,7 +82,9 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.inject.Inject; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; @@ -70,6 +95,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/fakeDriverTestContext.xml" }) public class SnapshotTestWithFakeData { @@ -93,31 +121,106 @@ public class SnapshotTestWithFakeData { VolumeService volumeService; @Inject VolumeDataFactory volumeDataFactory; + @Inject + DataCenterDao dcDao; + Long dcId; + @Inject + HostPodDao podDao; + Long podId; + @Inject + ClusterDao clusterDao; + Long clusterId; + @Inject + ImageStoreDao imageStoreDao; + ImageStoreVO imageStore; + @Inject + AccountManager accountManager; + LockMasterListener lockMasterListener; VolumeInfo vol = null; FakePrimaryDataStoreDriver driver = new FakePrimaryDataStoreDriver(); + @Inject + MockStorageMotionStrategy mockStorageMotionStrategy; + Merovingian2 _lockMaster; + @Inject + SnapshotPolicyDao snapshotPolicyDao; @Before public void setUp() { - Mockito.when(primaryDataStoreProvider.configure(Mockito.anyMap())).thenReturn(true); + // create data center + + DataCenterVO dc = new DataCenterVO(UUID.randomUUID().toString(), "test", "8.8.8.8", null, "10.0.0.1", null, + "10.0.0.1/24", null, null, DataCenter.NetworkType.Basic, null, null, true, true, null, null); + dc = dcDao.persist(dc); + dcId = dc.getId(); + // create pod + + HostPodVO pod = new HostPodVO(UUID.randomUUID().toString(), dc.getId(), "10.223.0.1", + "10.233.2.2/25", 8, "test"); + pod = podDao.persist(pod); + podId = pod.getId(); + // create xen cluster + ClusterVO cluster = new ClusterVO(dc.getId(), pod.getId(), "devcloud cluster"); + cluster.setHypervisorType(Hypervisor.HypervisorType.XenServer.toString()); + cluster.setClusterType(Cluster.ClusterType.CloudManaged); + cluster.setManagedState(Managed.ManagedState.Managed); + cluster = clusterDao.persist(cluster); + clusterId = cluster.getId(); + + imageStore = new ImageStoreVO(); + imageStore.setName(UUID.randomUUID().toString()); + imageStore.setDataCenterId(dcId); + imageStore.setProviderName(DataStoreProvider.NFS_IMAGE); + imageStore.setRole(DataStoreRole.Image); + imageStore.setUrl(UUID.randomUUID().toString()); + imageStore.setUuid(UUID.randomUUID().toString()); + imageStore.setProtocol("nfs"); + imageStore = imageStoreDao.persist(imageStore); + + when(primaryDataStoreProvider.configure(Mockito.anyMap())).thenReturn(true); Set types = new HashSet(); types.add(DataStoreProvider.DataStoreProviderType.PRIMARY); - Mockito.when(primaryDataStoreProvider.getTypes()).thenReturn(types); - Mockito.when(primaryDataStoreProvider.getName()).thenReturn(DataStoreProvider.DEFAULT_PRIMARY); - Mockito.when(primaryDataStoreProvider.getDataStoreDriver()).thenReturn(driver); + when(primaryDataStoreProvider.getTypes()).thenReturn(types); + when(primaryDataStoreProvider.getName()).thenReturn(DataStoreProvider.DEFAULT_PRIMARY); + when(primaryDataStoreProvider.getDataStoreDriver()).thenReturn(driver); + User user = mock(User.class); + when(user.getId()).thenReturn(1L); + Account account = mock(Account.class); + when(account.getId()).thenReturn(1L); + when(accountManager.getSystemAccount()).thenReturn(account); + when(accountManager.getSystemUser()).thenReturn(user); + if(Merovingian2.getLockMaster() == null) { + _lockMaster = Merovingian2.createLockMaster(1234); + } else { + _lockMaster = Merovingian2.getLockMaster(); + } + _lockMaster.cleanupThisServer(); ComponentContext.initComponentsLifeCycle(); } + + @After + public void tearDown() throws Exception { + _lockMaster.cleanupThisServer(); + } private SnapshotVO createSnapshotInDb() { - Snapshot.Type snapshotType = Snapshot.Type.MANUAL; - SnapshotVO snapshotVO = new SnapshotVO(1, 2, 1, 1L, 1L, UUID.randomUUID() + Snapshot.Type snapshotType = Snapshot.Type.RECURRING; + SnapshotVO snapshotVO = new SnapshotVO(dcId, 2, 1, 1L, 1L, UUID.randomUUID() + .toString(), (short) snapshotType.ordinal(), snapshotType.name(), 100, + Hypervisor.HypervisorType.XenServer); + return this.snapshotDao.persist(snapshotVO); + } + + private SnapshotVO createSnapshotInDb(Long volumeId) { + Snapshot.Type snapshotType = Snapshot.Type.DAILY; + SnapshotVO snapshotVO = new SnapshotVO(dcId, 2, 1, volumeId, 1L, UUID.randomUUID() .toString(), (short) snapshotType.ordinal(), snapshotType.name(), 100, Hypervisor.HypervisorType.XenServer); return this.snapshotDao.persist(snapshotVO); } private VolumeInfo createVolume(Long templateId, DataStore store) { - VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), 1L, 1L, 1L, 1L, 1000, 0L, 0L, ""); + VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), dcId, 1L, 1L, 1L, 1000, 0L, 0L, ""); ; volume.setPoolId(store.getId()); @@ -129,8 +232,8 @@ public class SnapshotTestWithFakeData { } private DataStore createDataStore() throws URISyntaxException { StoragePoolVO pool = new StoragePoolVO(); - pool.setClusterId(1L); - pool.setDataCenterId(1); + pool.setClusterId(clusterId); + pool.setDataCenterId(dcId); URI uri = new URI("nfs://jfkdkf/fjdkfj"); pool.setHostAddress(uri.getHost()); pool.setPath(uri.getPath()); @@ -139,14 +242,14 @@ public class SnapshotTestWithFakeData { pool.setUuid(UUID.randomUUID().toString()); pool.setStatus(StoragePoolStatus.Up); pool.setPoolType(Storage.StoragePoolType.NetworkFilesystem); - pool.setPodId(1L); + pool.setPodId(podId); pool.setScope(ScopeType.CLUSTER); pool.setStorageProviderName(DataStoreProvider.DEFAULT_PRIMARY); pool = this.primaryDataStoreDao.persist(pool); DataStore store = this.dataStoreManager.getPrimaryDataStore(pool.getId()); return store; } - @Test + //@Test public void testTakeSnapshot() throws URISyntaxException { SnapshotVO snapshotVO = createSnapshotInDb(); DataStore store = createDataStore(); @@ -167,7 +270,7 @@ public class SnapshotTestWithFakeData { } } - @Test + //@Test public void testTakeSnapshotWithFailed() throws URISyntaxException { SnapshotVO snapshotVO = createSnapshotInDb(); DataStore store = null; @@ -188,7 +291,7 @@ public class SnapshotTestWithFakeData { } } - @Test + //@Test public void testTakeSnapshotFromVolume() throws URISyntaxException { DataStore store = createDataStore(); FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); @@ -200,32 +303,57 @@ public class SnapshotTestWithFakeData { Assert.assertTrue(result == null); } + protected SnapshotPolicyVO createSnapshotPolicy(Long volId) { + SnapshotPolicyVO policyVO = new SnapshotPolicyVO(volId, "jfkd", "fdfd", DateUtil.IntervalType.DAILY, 8); + policyVO = snapshotPolicyDao.persist(policyVO); + return policyVO; + } + @Test public void testConcurrentSnapshot() throws URISyntaxException, InterruptedException, ExecutionException { DataStore store = createDataStore(); - FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); + final FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); dataStoreDriver.makeTakeSnapshotSucceed(true); final VolumeInfo volumeInfo = createVolume(1L, store); Assert.assertTrue(volumeInfo.getState() == Volume.State.Ready); vol = volumeInfo; + // final SnapshotPolicyVO policyVO = createSnapshotPolicy(vol.getId()); + + ExecutorService pool = Executors.newFixedThreadPool(2); boolean result = false; - Future future = null; - for(int i = 0; i < 1; i++) { - future = pool.submit(new Callable() { + List> future = new ArrayList>(); + for(int i = 0; i < 12; i++) { + final int cnt = i; + Future task = pool.submit(new Callable() { @Override public Boolean call() throws Exception { - boolean r = false; + boolean r = true; try { + SnapshotVO snapshotVO = createSnapshotInDb(vol.getId()); + VolumeObject volumeObject = (VolumeObject)vol; + Account account = mock(Account.class); + when(account.getId()).thenReturn(1L); + CreateSnapshotPayload createSnapshotPayload = mock(CreateSnapshotPayload.class); + when(createSnapshotPayload.getAccount()).thenReturn(account); + when(createSnapshotPayload.getSnapshotId()).thenReturn(snapshotVO.getId()); + when(createSnapshotPayload.getSnapshotPolicyId()).thenReturn(0L); + volumeObject.addPayload(createSnapshotPayload); + if (cnt > 8) { + mockStorageMotionStrategy.makeBackupSnapshotSucceed(false); + } SnapshotInfo newSnapshot = volumeService.takeSnapshot(vol); - Assert.assertTrue(newSnapshot != null); + if (newSnapshot == null) { + r = false; + } } catch (Exception e) { r = false; } - return true; + return r; } }); + Assert.assertTrue(task.get()); } - future.get(); + } } diff --git a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml index c3c8ef5a6bb..b7ef363ff04 100644 --- a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml +++ b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml @@ -86,5 +86,6 @@ + diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 5653ab4d16c..fbf908184dc 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.storage.snapshot; import javax.inject.Inject; +import com.cloud.utils.db.DB; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; @@ -189,7 +190,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { if (!Snapshot.State.BackedUp.equals(snapshotVO.getState())) { throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId - + " due to it is not in BackedUp Status"); + + " due to it is in " + snapshotVO.getState() + " Status"); } // first mark the snapshot as destroyed, so that ui can't see it, but we @@ -235,40 +236,53 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { } @Override + @DB public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { - SnapshotResult result = snapshotSvr.takeSnapshot(snapshot); - if (result.isFailed()) { - s_logger.debug("Failed to take snapshot: " + result.getResult()); - throw new CloudRuntimeException(result.getResult()); + SnapshotVO snapshotVO = snapshotDao.acquireInLockTable(snapshot.getId()); + if (snapshotVO == null) { + throw new CloudRuntimeException("Failed to get lock on snapshot:" + snapshot.getId()); } - snapshot = result.getSnashot(); - DataStore primaryStore = snapshot.getDataStore(); - SnapshotInfo backupedSnapshot = this.backupSnapshot(snapshot); try { - SnapshotInfo parent = snapshot.getParent(); - if (backupedSnapshot != null && parent != null) { - Long parentSnapshotId = parent.getId(); - while (parentSnapshotId != null && parentSnapshotId != 0L) { - SnapshotDataStoreVO snapshotDataStoreVO = snapshotStoreDao.findByStoreSnapshot(primaryStore.getRole(),primaryStore.getId(), parentSnapshotId); + SnapshotResult result = snapshotSvr.takeSnapshot(snapshot); + if (result.isFailed()) { + s_logger.debug("Failed to take snapshot: " + result.getResult()); + throw new CloudRuntimeException(result.getResult()); + } + snapshot = result.getSnashot(); + DataStore primaryStore = snapshot.getDataStore(); + + SnapshotInfo backupedSnapshot = this.backupSnapshot(snapshot); + + try { + SnapshotInfo parent = snapshot.getParent(); + if (backupedSnapshot != null && parent != null) { + Long parentSnapshotId = parent.getId(); + while (parentSnapshotId != null && parentSnapshotId != 0L) { + SnapshotDataStoreVO snapshotDataStoreVO = snapshotStoreDao.findByStoreSnapshot(primaryStore.getRole(),primaryStore.getId(), parentSnapshotId); + if (snapshotDataStoreVO != null) { + parentSnapshotId = snapshotDataStoreVO.getParentSnapshotId(); + snapshotStoreDao.remove(snapshotDataStoreVO.getId()); + } else { + parentSnapshotId = null; + } + } + SnapshotDataStoreVO snapshotDataStoreVO = snapshotStoreDao.findByStoreSnapshot(primaryStore.getRole(), primaryStore.getId(), + snapshot.getId()); if (snapshotDataStoreVO != null) { - parentSnapshotId = snapshotDataStoreVO.getParentSnapshotId(); - snapshotStoreDao.remove(snapshotDataStoreVO.getId()); - } else { - parentSnapshotId = null; + snapshotDataStoreVO.setParentSnapshotId(0L); + snapshotStoreDao.update(snapshotDataStoreVO.getId(), snapshotDataStoreVO); } } - SnapshotDataStoreVO snapshotDataStoreVO = snapshotStoreDao.findByStoreSnapshot(primaryStore.getRole(), primaryStore.getId(), - snapshot.getId()); - if (snapshotDataStoreVO != null) { - snapshotDataStoreVO.setParentSnapshotId(0L); - snapshotStoreDao.update(snapshotDataStoreVO.getId(), snapshotDataStoreVO); - } + } catch (Exception e) { + s_logger.debug("Failed to clean up snapshots on primary storage", e); + } + return backupedSnapshot; + } finally { + if (snapshotVO != null) { + snapshotDao.releaseFromLockTable(snapshot.getId()); } - } catch (Exception e) { - s_logger.debug("Failed to clean up snapshots on primary storage", e); } - return backupedSnapshot; } @Override diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index 1935a881ded..d8280851a38 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -25,7 +25,8 @@ import java.util.Map; import javax.naming.ConfigurationException; -import com.cloud.utils.db.SearchCriteria2; + +import com.cloud.utils.db.DB; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; @@ -179,6 +180,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase maxSnaps && snaps.size() > 1) { SnapshotVO oldestSnapshot = snaps.get(0); long oldSnapId = oldestSnapshot.getId(); - s_logger.debug("Max snaps: " + policy.getMaxSnaps() + " exceeded for snapshot policy with Id: " + policyId + ". Deleting oldest snapshot: " + oldSnapId); + if (policy != null) { + s_logger.debug("Max snaps: " + policy.getMaxSnaps() + " exceeded for snapshot policy with Id: " + policyId + ". Deleting oldest snapshot: " + oldSnapId); + } if(deleteSnapshot(oldSnapId)){ //log Snapshot delete event ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, oldestSnapshot.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_SNAPSHOT_DELETE, "Successfully deleted oldest snapshot: " + oldSnapId, 0); @@ -997,6 +999,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return true; } @Override + @DB public SnapshotInfo takeSnapshot(VolumeInfo volume) throws ResourceAllocationException { CreateSnapshotPayload payload = (CreateSnapshotPayload)volume.getpayload(); Long snapshotId = payload.getSnapshotId(); @@ -1015,15 +1018,17 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (!processed) { throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshotId); } - postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), - snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, - volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); - - - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + try { + postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), + snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, + volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); + _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + } catch (Exception e) { + s_logger.debug("post process snapshot failed", e); + } } catch(Exception e) { s_logger.debug("Failed to create snapshot", e); if (backup) { From f4dbf8786fe1ab2bd7ba822a9ed4318303855368 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:08:41 -0700 Subject: [PATCH 11/33] CLOUDSTACK-4477: in order to select hypervisor host which can access storage pool, need to check storage_pool_host_ref Conflicts: engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java engine/storage/integration-test/test/org/apache/cloudstack/storage/test/EndpointSelectorTest.java engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java --- .../storage/test/DirectAgentManagerSimpleImpl.java | 5 +++++ .../cloudstack/storage/test/EndpointSelectorTest.java | 8 ++++++++ .../storage/test/FakeDriverTestConfiguration.java | 2 -- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java index ec681192aae..fc926af558d 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java @@ -26,6 +26,10 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.host.Host; +import com.cloud.host.Status; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.utils.fsm.StateMachine2; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -246,6 +250,7 @@ public class DirectAgentManagerSimpleImpl extends ManagerBase implements AgentMa e1.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } return true; + } @Override diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/EndpointSelectorTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/EndpointSelectorTest.java index d645deb1b4e..7b8dd3ab642 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/EndpointSelectorTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/EndpointSelectorTest.java @@ -18,6 +18,7 @@ */ package org.apache.cloudstack.storage.test; + import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -28,6 +29,7 @@ import java.util.UUID; import javax.inject.Inject; +import com.cloud.server.LockMasterListener; import junit.framework.Assert; import org.junit.Before; @@ -58,6 +60,9 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import com.cloud.agent.AgentManager; + +import com.cloud.agent.AgentManager; + import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; @@ -88,6 +93,7 @@ import com.cloud.user.User; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.Merovingian2; + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/fakeDriverTestContext.xml" }) public class EndpointSelectorTest { @@ -121,6 +127,8 @@ public class EndpointSelectorTest { ImageStoreVO imageStore; @Inject AccountManager accountManager; + + LockMasterListener lockMasterListener; VolumeInfo vol = null; FakePrimaryDataStoreDriver driver = new FakePrimaryDataStoreDriver(); @Inject diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java index 5e3e8851066..9022d9703ab 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java @@ -24,7 +24,6 @@ import com.cloud.storage.snapshot.SnapshotSchedulerImpl; import com.cloud.user.DomainManager; import com.cloud.utils.component.ComponentContext; - import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -61,7 +60,6 @@ public class FakeDriverTestConfiguration extends ChildTestConfiguration{ return Mockito.mock(DomainManager.class); } - @Override @Bean public EndPointSelector selector() { From aa59b6f34d1156a97d4c3810885e18c7adcd444f Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 26 Aug 2013 11:04:28 -0700 Subject: [PATCH 12/33] CLOUDSTACK-4507: fix NPE when taking snapshot --- .../org/apache/cloudstack/storage/snapshot/SnapshotObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index e69881c6006..7d823a3b407 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -265,7 +265,7 @@ public class SnapshotObject implements SnapshotInfo { this.snapshotStoreDao.update(snapshotStore.getId(), snapshotStore); // update side-effect of snapshot operation - if(snapshotTO.getVolume().getPath() != null) { + if(snapshotTO.getVolume() != null && snapshotTO.getVolume().getPath() != null) { VolumeVO vol = this.volumeDao.findByUuid(snapshotTO.getVolume().getUuid()); if(vol != null) { s_logger.info("Update volume path change due to snapshot operation, volume " + vol.getId() + " path: " From 9fa56e28ec0c6bd4b71de6f0804f58cf4bc94aae Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 26 Aug 2013 11:15:30 -0700 Subject: [PATCH 13/33] CLOUDSTACK-4459: need to sychronize create storage pool, otherwise, we can create the same pool with the same uuid in parallel --- .../cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 71bbc15e37c..7fef6fffb51 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 @@ -173,7 +173,8 @@ public class KVMStoragePoolManager { return createStoragePool(name, host, port, path, userInfo, type, true); } - private KVMStoragePool createStoragePool( String name, String host, int port, + //Note: due to bug CLOUDSTACK-4459, createStoragepool can be called in parallel, so need to be synced. + private synchronized KVMStoragePool createStoragePool( String name, String host, int port, String path, String userInfo, StoragePoolType type, boolean primaryStorage) { StorageAdaptor adaptor = getStorageAdaptor(type); From dda1133f1210c25f8fc4ed4098be5e941d7a51d6 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:10:57 -0700 Subject: [PATCH 14/33] CLOUDSTACK-4436: in case of older kvm host, we'd better try serveral times to make sure we passed cmdline parameters to system vms Conflicts: core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java --- .../VirtualRoutingResource.java | 35 +++++++++++++++++++ .../resource/LibvirtComputingResource.java | 19 +++++----- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 0592a9b3bee..479640a32b8 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -1215,6 +1215,41 @@ public class VirtualRoutingResource implements Manager { return "Unable to connect"; } + public boolean connect(final String ipAddress, int retry, int sleep) { + for (int i = 0; i <= retry; i++) { + SocketChannel sch = null; + try { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Trying to connect to " + ipAddress); + } + sch = SocketChannel.open(); + sch.configureBlocking(true); + + final InetSocketAddress addr = new InetSocketAddress(ipAddress, _port); + sch.connect(addr); + return true; + } catch (final IOException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Could not connect to " + ipAddress); + } + } finally { + if (sch != null) { + try { + sch.close(); + } catch (final IOException e) {} + } + } + try { + Thread.sleep(sleep); + } catch (final InterruptedException e) { + } + } + + s_logger.debug("Unable to logon to " + ipAddress); + + return false; + } + @Override public String getName() { return _name; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 6a7a56251a1..61fe71d7424 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3534,15 +3534,18 @@ ServerResource { if (vmSpec.getType() != VirtualMachine.Type.User) { if ((_kernelVersion < 2006034) && (conn.getVersion() < 1001000)) { // CLOUDSTACK-2823: try passCmdLine some times if kernel < 2.6.34 and qemu < 1.1.0 on hypervisor (for instance, CentOS 6.4) //wait for 5 minutes at most - for (int count = 0; count < 30; count ++) { - boolean succeed = passCmdLine(vmName, vmSpec.getBootArgs()); - if (succeed) { - break; + String controlIp = null; + for (NicTO nic : nics) { + if (nic.getType() == TrafficType.Control) { + controlIp = nic.getIp(); } - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - s_logger.trace("Ignoring InterruptedException.", e); + } + for (int count = 0; count < 30; count ++) { + passCmdLine(vmName, vmSpec.getBootArgs()); + //check router is up? + boolean result = _virtRouterResource.connect(controlIp, 1, 5000); + if (result) { + break; } } } else { From b695484dc75647d01ac59c14fb859a8b0338c819 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 26 Aug 2013 18:07:18 -0700 Subject: [PATCH 15/33] CLOUDSTACK-4459: Libvirt reports: org.libvirt.LibvirtException: Storage volume not found: no storage vol with matching name in some cases, if the volume is created on one kvm host, while accessed from other host. It's possible due to concurrent access(read/write) storage. The current fix is to try serveral times, and wait for 30 seconds for each retry. If the issue still there, then need to sync the storage pool access --- .../kvm/storage/KVMStoragePoolManager.java | 8 ++++++-- .../kvm/storage/LibvirtStorageAdaptor.java | 13 ++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) 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 7fef6fffb51..e09c9ba44da 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 @@ -97,6 +97,7 @@ public class KVMStoragePoolManager { } public KVMStoragePool getStoragePool(StoragePoolType type, String uuid) { + StorageAdaptor adaptor = getStorageAdaptor(type); KVMStoragePool pool = null; try { @@ -149,12 +150,15 @@ public class KVMStoragePoolManager { if (vol != null) { break; } - - Thread.sleep(10000); } catch (Exception e) { s_logger.debug("Failed to find volume:" + volName + " due to" + e.toString() + ", retry:" + cnt); errMsg = e.toString(); } + + try { + Thread.sleep(30000); + } catch (InterruptedException e) { + } cnt++; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 53eb80a922b..b2bdd5b1d42 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -101,10 +101,15 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { try { vol = pool.storageVolLookupByName(volName); } catch (LibvirtException e) { - + s_logger.debug("Can't find volume: " + e.toString()); } if (vol == null) { - storagePoolRefresh(pool); + try { + refreshPool(pool); + } catch (LibvirtException e) { + s_logger.debug("failed to refresh pool: " + e.toString()); + } + try { vol = pool.storageVolLookupByName(volName); } catch (LibvirtException e) { @@ -119,6 +124,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { LibvirtStorageVolumeDef volDef = new LibvirtStorageVolumeDef(UUID .randomUUID().toString(), size, format, null, null); s_logger.debug(volDef.toString()); + return pool.storageVolCreateXML(volDef.toString(), 0); } @@ -128,7 +134,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { refreshPool(pool); } } catch (LibvirtException e) { - + s_logger.debug("refresh storage pool failed: " + e.toString()); } } @@ -438,6 +444,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } return disk; } catch (LibvirtException e) { + s_logger.debug("Failed to get physical disk:", e); throw new CloudRuntimeException(e.toString()); } From 49d8e4449166f22e30bfd13dc97827f283306e06 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:16:10 -0700 Subject: [PATCH 16/33] CLOUDSTACK-4459: one more try, bypass libvirt to find volume if libvirt call failed Conflicts: server/src/com/cloud/template/TemplateManagerImpl.java --- .../kvm/storage/LibvirtStoragePool.java | 32 ++++++++++++- .../cloud/template/TemplateManagerImpl.java | 47 ++++++++++++------- 2 files changed, 61 insertions(+), 18 deletions(-) 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 2ce517504d6..dc7981c336b 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 @@ -16,14 +16,19 @@ // under the License. package com.cloud.hypervisor.kvm.storage; +import java.io.File; import java.util.List; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.log4j.Logger; import org.libvirt.StoragePool; import com.cloud.storage.Storage.StoragePoolType; public class LibvirtStoragePool implements KVMStoragePool { + private static final Logger s_logger = Logger + .getLogger(LibvirtStoragePool.class); protected String uuid; protected String uri; protected long capacity; @@ -120,7 +125,32 @@ public class LibvirtStoragePool implements KVMStoragePool { @Override public KVMPhysicalDisk getPhysicalDisk(String volumeUuid) { - return this._storageAdaptor.getPhysicalDisk(volumeUuid, this); + KVMPhysicalDisk disk = null; + try { + disk = this._storageAdaptor.getPhysicalDisk(volumeUuid, this); + } catch (CloudRuntimeException e) { + if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) || + (this.getStoragePoolType() != StoragePoolType.Filesystem)) { + throw e; + } + } + + if (disk != null) { + return disk; + } + s_logger.debug("find volume bypass libvirt"); + //For network file system or file system, try to use java file to find the volume, instead of through libvirt. BUG:CLOUDSTACK-4459 + String localPoolPath = this.getLocalPath(); + File f = new File(localPoolPath + File.separator + volumeUuid); + if (!f.exists()) { + s_logger.debug("volume: " + volumeUuid + " not exist on storage pool"); + throw new CloudRuntimeException("Can't find volume:" + volumeUuid); + } + disk = new KVMPhysicalDisk(f.getPath(), volumeUuid, this); + disk.setFormat(PhysicalDiskFormat.QCOW2); + disk.setSize(f.length()); + disk.setVirtualSize(f.length()); + return disk; } @Override diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index e11ac0db3c2..c52c323cb58 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -770,29 +770,42 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } @Override + @DB public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) { - StoragePool pool = (StoragePool) _dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); - VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Evicting " + templatePoolVO); + //Need to hold the lock, otherwise, another thread may create a volume from the template at the same time. + //Assumption here is that, we will hold the same lock during create volume from template + VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolVO.getId()); + if (templatePoolRef == null) { + s_logger.debug("can't aquire the lock for template pool ref:" + templatePoolVO.getId()); + return; } - DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); try { - Answer answer = _storageMgr.sendToPool(pool, cmd); + StoragePool pool = (StoragePool) this._dataStoreMgr.getPrimaryDataStore(templatePoolVO.getPoolId()); + VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId()); - if (answer != null && answer.getResult()) { - // Remove the templatePoolVO - if (_tmpltPoolDao.remove(templatePoolVO.getId())) { - s_logger.debug("Successfully evicted template: " + template.getName() + " from storage pool: " + pool.getName()); - } - } else { - s_logger.info("Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Evicting " + templatePoolVO); } - } catch (StorageUnavailableException e) { - s_logger.info("Storage is unavailable currently. Will retry evicte template: " + template.getName() + " from storage pool: " - + pool.getName()); + DestroyCommand cmd = new DestroyCommand(pool, templatePoolVO); + + try { + Answer answer = _storageMgr.sendToPool(pool, cmd); + + if (answer != null && answer.getResult()) { + // Remove the templatePoolVO + if (_tmpltPoolDao.remove(templatePoolVO.getId())) { + s_logger.debug("Successfully evicted template: " + template.getName() + " from storage pool: " + pool.getName()); + } + } else { + s_logger.info("Will retry evicte template: " + template.getName() + " from storage pool: " + pool.getName()); + } + } catch (StorageUnavailableException e) { + s_logger.info("Storage is unavailable currently. Will retry evicte template: " + template.getName() + " from storage pool: " + + pool.getName()); + } + } finally { + _tmpltPoolDao.releaseFromLockTable(templatePoolRef.getId()); } } From 6489b3bf618f661e1606461a3b66a05b61d369b1 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 27 Aug 2013 15:04:24 -0700 Subject: [PATCH 17/33] turn off nfs cache --- python/lib/cloudutils/serviceConfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/lib/cloudutils/serviceConfig.py b/python/lib/cloudutils/serviceConfig.py index d129e00c45b..5c552c0b756 100755 --- a/python/lib/cloudutils/serviceConfig.py +++ b/python/lib/cloudutils/serviceConfig.py @@ -388,7 +388,8 @@ class nfsConfig(serviceCfgBase): return True cfo = configFileOps("/etc/nfsmount.conf") - cfo.addEntry("AC", "False") + cfo.addEntry("Ac", "False") + cfo.addEntry("actimeo", "0") cfo.save() self.syscfg.svo.enableService("rpcbind") From 2227eb191aa6956088e522ceaba68a4d0e104221 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Tue, 27 Aug 2013 18:08:06 -0700 Subject: [PATCH 18/33] CLOUDSTACK-3535: fix regression introduced in 5d9fa5d42e13a8c8ee10df14c466f66de678e954 --- .../com/cloud/vm/VirtualMachineManagerImpl.java | 8 ++++++-- .../storage/volume/VolumeServiceImpl.java | 1 + .../kvm/src/com/cloud/ha/KVMInvestigator.java | 14 +++++++++----- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 24f0795fcc1..3829336d28b 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1203,10 +1203,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (AgentUnavailableException e) { s_logger.warn("Unable to stop vm, agent unavailable: " + e.toString()); - throw e; + if (!forced) { + throw e; + } } catch (OperationTimedoutException e) { s_logger.warn("Unable to stop vm, operation timed out: " + e.toString()); - throw e; + if (!forced) { + throw e; + } } finally { if (!stopped) { if (!cleanUpEvenIfUnableToStop) { diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index eab79b85f2b..28142ea701c 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -518,6 +518,7 @@ public class VolumeServiceImpl implements VolumeService { if (result.isSuccess()) { vo.processEvent(Event.OperationSuccessed, result.getAnswer()); } else { + vo.processEvent(Event.OperationFailed); volResult.setResult(result.getResult()); // hack for Vmware: host is down, previously download template to the host needs to be re-downloaded, so we need to reset diff --git a/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java b/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java index 4d83d099e78..e173f32bdd5 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java +++ b/plugins/hypervisors/kvm/src/com/cloud/ha/KVMInvestigator.java @@ -58,15 +58,19 @@ public class KVMInvestigator extends AdapterBase implements Investigator { return null; } CheckOnHostCommand cmd = new CheckOnHostCommand(agent); - List neighbors = _resourceMgr.listAllHostsInCluster(agent.getClusterId()); + List neighbors = _resourceMgr.listHostsInClusterByStatus(agent.getClusterId(), Status.Up); for (HostVO neighbor : neighbors) { if (neighbor.getId() == agent.getId() || neighbor.getHypervisorType() != Hypervisor.HypervisorType.KVM) { continue; } - Answer answer = _agentMgr.easySend(neighbor.getId(), cmd); - - return answer.getResult() ? Status.Down : Status.Up; - + try { + Answer answer = _agentMgr.easySend(neighbor.getId(), cmd); + if (answer != null) { + return answer.getResult() ? Status.Down : Status.Up; + } + } catch (Exception e) { + s_logger.debug("Failed to send command to host: " + neighbor.getId()); + } } return null; From 88114350c7a126bd943f3841d0cfe79563525311 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 29 Aug 2013 10:33:47 -0700 Subject: [PATCH 19/33] CLOUDSTACK-4559: fix devcloud --- .../vm/hypervisor/xenserver/xcposs/vmopsSnapshot | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot index 31f26ad3c3e..53f31a99eed 100644 --- a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot +++ b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot @@ -372,8 +372,16 @@ def unmountSnapshotsDir(session, args): return "1" -def getPrimarySRPath(primaryStorageSRUuid, isISCSI): - if isISCSI: +def getPrimarySRPath(session, primaryStorageSRUuid, isISCSI): + sr = session.xenapi.SR.get_by_uuid(primaryStorageSRUuid) + srrec = session.xenapi.SR.get_record(sr) + srtype = srrec["type"] + if srtype == "file": + pbd = session.xenapi.SR.get_PBDs(sr)[0] + pbdrec = session.xenapi.PBD.get_record(pbd) + primarySRPath = pbdrec["device_config"]["location"] + return primarySRPath + elif isISCSI: primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid return os.path.join(lvhdutil.VG_LOCATION, primarySRDir) else: @@ -472,7 +480,7 @@ def getVhdParent(session, args): snapshotUuid = args['snapshotUuid'] isISCSI = getIsTrueString(args['isISCSI']) - primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) util.SMlog("primarySRPath: " + primarySRPath) baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) @@ -490,7 +498,7 @@ def backupSnapshot(session, args): isISCSI = getIsTrueString(args['isISCSI']) path = args['path'] localMountPoint = args['localMountPoint'] - primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI) util.SMlog("primarySRPath: " + primarySRPath) baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) From a96f6b306b22ef4d17dae66cc754434cd71f9fc7 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 29 Aug 2013 14:24:35 -0700 Subject: [PATCH 20/33] CLOUDSTACK-4459: fix silly bug, one more time --- .../com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dc7981c336b..c0e10002930 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 @@ -129,7 +129,7 @@ public class LibvirtStoragePool implements KVMStoragePool { try { disk = this._storageAdaptor.getPhysicalDisk(volumeUuid, this); } catch (CloudRuntimeException e) { - if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) || + if ((this.getStoragePoolType() != StoragePoolType.NetworkFilesystem) && (this.getStoragePoolType() != StoragePoolType.Filesystem)) { throw e; } From 3ddc6da10be72ad292dd18ef0a893c67f853f6c4 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:19:53 -0700 Subject: [PATCH 21/33] CLOUDSTACK-4405: fix vm migration during the upgrade to 4.2 Conflicts: plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java --- .../src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index 9ebf92672ea..853ffaa281d 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -45,6 +45,7 @@ public class BridgeVifDriver extends VifDriverBase { private static final Object _vnetBridgeMonitor = new Object(); private String _modifyVlanPath; + private String bridgeNameSchema; @Override public void configure(Map params) throws ConfigurationException { @@ -59,6 +60,8 @@ public class BridgeVifDriver extends VifDriverBase { networkScriptsDir = "scripts/vm/network/vnet"; } + bridgeNameSchema = (String) params.get("network.bridge.name.schema"); + String value = (String) params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000; From 0eb3944fc8108b3a8bf161bad692f6f58fb5835b Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 30 Aug 2013 18:06:08 -0700 Subject: [PATCH 22/33] kvm upgrade issue from 2.2.14: 1. the uuid passed by mgt server is malformat, libvirt can't start vm. 2. the template path on primary storage is incorrect, which contains absolute path --- .../resource/LibvirtComputingResource.java | 20 ++++++++++++++++++- .../kvm/storage/KVMStorageProcessor.java | 4 ++++ .../LibvirtComputingResourceTest.java | 15 ++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 61fe71d7424..04499d58851 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3357,10 +3357,28 @@ ServerResource { } } + protected String getUuid(String uuid) { + if (uuid == null) { + uuid = UUID.randomUUID().toString(); + } else { + try { + UUID uuid2 = UUID.fromString(uuid); + String uuid3 = uuid2.toString(); + if (!uuid3.equals(uuid)) { + uuid = UUID.randomUUID().toString(); + } + } catch (IllegalArgumentException e) { + uuid = UUID.randomUUID().toString(); + } + } + return uuid; + } protected LibvirtVMDef createVMFromSpec(VirtualMachineTO vmTO) { LibvirtVMDef vm = new LibvirtVMDef(); vm.setDomainName(vmTO.getName()); - vm.setDomUUID(vmTO.getUuid()); + String uuid = vmTO.getUuid(); + uuid = getUuid(uuid); + vm.setDomUUID(uuid); vm.setDomDescription(vmTO.getOs()); GuestDef guest = new GuestDef(); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 5ed93aab496..383d21118ff 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -289,6 +289,10 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { vol = templateToPrimaryDownload(templatePath, primaryPool); } else { + if (templatePath.contains("/mnt")) { + //upgrade issue, if the path contains path, need to extract the volume uuid from path + templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1); + } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); } diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 3640030ad8c..d6e8dc2fcc2 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -24,11 +24,13 @@ import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; +import junit.framework.Assert; import org.apache.commons.lang.SystemUtils; import org.junit.Assume; import org.junit.Test; import java.util.Random; +import java.util.UUID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -195,4 +197,17 @@ public class LibvirtComputingResourceTest { Pair stats = LibvirtComputingResource.getNicStats("lo"); assertNotNull(stats); } + + @Test + public void testUUID() { + String uuid = "1"; + LibvirtComputingResource lcr = new LibvirtComputingResource(); + uuid =lcr.getUuid(uuid); + Assert.assertTrue(!uuid.equals("1")); + + String oldUuid = UUID.randomUUID().toString(); + uuid = oldUuid; + uuid = lcr.getUuid(uuid); + Assert.assertTrue(uuid.equals(oldUuid)); + } } From d1a14fbf9549aa6c25526729fc32c6a5fb20b364 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:29:01 -0700 Subject: [PATCH 23/33] disk resize NPE, if the new disk offering doesn't have tags, then NPE --- server/src/com/cloud/storage/VolumeApiServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index cc995892eb3..16eecd60bc3 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -757,7 +757,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } if (diskOffering.getTags() != null) { - if (!newDiskOffering.getTags().equals(diskOffering.getTags())) { + if (newDiskOffering.getTags() == null || !newDiskOffering.getTags().equals(diskOffering.getTags())) { throw new InvalidParameterValueException("Tags on new and old disk offerings must match"); } } else if (newDiskOffering.getTags() != null) { From 4fb459355337c874a10f47c0224af72d6fef1ff2 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 6 Sep 2013 17:55:11 -0700 Subject: [PATCH 24/33] CLOUDSTACK-4618: fix CLVM --- .../kvm/storage/KVMStorageProcessor.java | 31 +++++++--- .../CloudStackPrimaryDataStoreDriverImpl.java | 57 ++++++++++++++++++- scripts/storage/qcow2/managesnapshot.sh | 4 +- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 383d21118ff..a74df193a08 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -147,8 +147,7 @@ public class KVMStorageProcessor implements StorageProcessor { DataTO destData = cmd.getDestTO(); TemplateObjectTO template = (TemplateObjectTO) srcData; DataStoreTO imageStore = template.getDataStore(); - TemplateObjectTO volume = (TemplateObjectTO) destData; - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) volume.getDataStore(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO) destData.getDataStore(); if (!(imageStore instanceof NfsTO)) { return new CopyCmdAnswer("unsupported protocol"); @@ -197,19 +196,32 @@ public class KVMStorageProcessor implements StorageProcessor { KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, UUID.randomUUID().toString(), primaryPool); - TemplateObjectTO newTemplate = new TemplateObjectTO(); - newTemplate.setPath(primaryVol.getName()); + DataTO data = null; /** * Force the ImageFormat for RBD templates to RAW * */ - if (primaryPool.getType() == StoragePoolType.RBD) { - newTemplate.setFormat(ImageFormat.RAW); - } else { - newTemplate.setFormat(ImageFormat.QCOW2); + if (destData.getObjectType() == DataObjectType.TEMPLATE) { + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(primaryVol.getName()); + if (primaryPool.getType() == StoragePoolType.RBD) { + newTemplate.setFormat(ImageFormat.RAW); + } else { + newTemplate.setFormat(ImageFormat.QCOW2); + } + data = newTemplate; + } else if (destData.getObjectType() == DataObjectType.VOLUME) { + VolumeObjectTO volumeObjectTO = new VolumeObjectTO(); + volumeObjectTO.setPath(primaryVol.getName()); + if (primaryVol.getFormat() == PhysicalDiskFormat.RAW) + volumeObjectTO.setFormat(ImageFormat.RAW); + else if (primaryVol.getFormat() == PhysicalDiskFormat.QCOW2) { + volumeObjectTO.setFormat(ImageFormat.QCOW2); + } + data = volumeObjectTO; } - return new CopyCmdAnswer(newTemplate); + return new CopyCmdAnswer(data); } catch (CloudRuntimeException e) { return new CopyCmdAnswer(e.toString()); } finally { @@ -287,6 +299,7 @@ public class KVMStorageProcessor implements StorageProcessor { String templatePath = template.getPath(); if (primaryPool.getType() == StoragePoolType.CLVM) { + templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; vol = templateToPrimaryDownload(templatePath, primaryPool); } else { if (templatePath.contains("/mnt")) { diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index a854d2ef415..96d3ac0d3cf 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -25,9 +25,13 @@ import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.dao.HostDao; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; +import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.dao.DiskOfferingDao; @@ -35,20 +39,27 @@ import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.template.TemplateManager; +import com.cloud.utils.NumbersUtil; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; import javax.inject.Inject; +import java.util.UUID; public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { private static final Logger s_logger = Logger.getLogger(CloudStackPrimaryDataStoreDriverImpl.class); @@ -74,7 +85,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri SnapshotManager snapshotMgr; @Inject EndPointSelector epSelector; - + @Inject + ConfigurationDao configDao; + @Inject + TemplateManager templateManager; + @Inject + TemplateDataFactory templateDataFactory; @Override public DataTO getTO(DataObject data) { return null; @@ -165,10 +181,49 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback) { + DataStore store = destData.getDataStore(); + if (store.getRole() == DataStoreRole.Primary) { + if ((srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE)) { + //For CLVM, we need to copy template to primary storage at all, just fake the copy result. + TemplateObjectTO templateObjectTO = new TemplateObjectTO(); + templateObjectTO.setPath(UUID.randomUUID().toString()); + templateObjectTO.setSize(srcdata.getSize()); + templateObjectTO.setPhysicalSize(srcdata.getSize()); + templateObjectTO.setFormat(Storage.ImageFormat.RAW); + CopyCmdAnswer answer = new CopyCmdAnswer(templateObjectTO); + CopyCommandResult result = new CopyCommandResult("", answer); + callback.complete(result); + } else if (srcdata.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME) { + //For CLVM, we need to pass template on secondary storage to hypervisor + String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); + int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, + Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue())); + StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); + DataStore imageStore = templateManager.getImageStore(storagePoolVO.getDataCenterId(), srcdata.getId()); + DataObject srcData = templateDataFactory.getTemplate(srcdata.getId(), imageStore); + + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _primaryStorageDownloadWait, true); + EndPoint ep = epSelector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); + CopyCommandResult result = new CopyCommandResult("", answer); + callback.complete(result); + } + } } @Override public boolean canCopy(DataObject srcData, DataObject destData) { + //BUG fix for CLOUDSTACK-4618 + DataStore store = destData.getDataStore(); + if (store.getRole() == DataStoreRole.Primary) { + if ((srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.TEMPLATE) || + (srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME)) { + StoragePoolVO storagePoolVO = primaryStoreDao.findById(store.getId()); + if (storagePoolVO != null && storagePoolVO.getPoolType() == Storage.StoragePoolType.CLVM) { + return true; + } + } + } return false; } diff --git a/scripts/storage/qcow2/managesnapshot.sh b/scripts/storage/qcow2/managesnapshot.sh index 368ff549ee6..42bd1eb2613 100755 --- a/scripts/storage/qcow2/managesnapshot.sh +++ b/scripts/storage/qcow2/managesnapshot.sh @@ -42,11 +42,11 @@ fi is_lv() { # Must be a block device - if [ -b "${1}" ]; then + if [ -b "${1}" -o -L "{1}" ]; then # But not a volume group or physical volume lvm vgs "${1}" > /dev/null 2>&1 && return 1 # And a logical volume - lvm lvs "${1}" > /dev/null 2>&1 && return 0 + lvm lvs "${1}" > /dev/null 2>&1 && return 1 fi return 0 } From 7d8a7f855df47c645746d1ab35dd379ec1ed60e4 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:32:40 -0700 Subject: [PATCH 25/33] CLOUDSTACK-4627: fix NPE in vm migration --- .../cloudstack/engine/orchestration/VolumeOrchestrator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 088c943e6c0..7258e2557f0 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -1084,7 +1084,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati public boolean canVmRestartOnAnotherServer(long vmId) { List vols = _volsDao.findCreatedByInstance(vmId); for (VolumeVO vol : vols) { - if (!vol.isRecreatable() && !vol.getPoolType().isShared()) { + StoragePoolVO storagePoolVO = _storagePoolDao.findById(vol.getPoolId()); + if (!vol.isRecreatable() && storagePoolVO != null && storagePoolVO.getPoolType() != null && !(storagePoolVO.getPoolType().isShared())) { return false; } } From 3420f2d0167341011d994cddb98a94a79b5eb308 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 11 Sep 2013 18:07:47 -0700 Subject: [PATCH 26/33] CLOUDSTACK-4641: fix create volume from snapshot timeout issue --- .../storage/command/CopyCommand.java | 4 +++ .../resource/LibvirtComputingResource.java | 14 +++++----- .../kvm/storage/KVMStoragePoolManager.java | 14 +++++----- .../kvm/storage/KVMStorageProcessor.java | 26 +++++++++---------- .../kvm/storage/LibvirtStorageAdaptor.java | 25 ++++++++++-------- .../kvm/storage/StorageAdaptor.java | 4 +-- .../apache/cloudstack/utils/qemu/QemuImg.java | 11 +++++--- .../cloudstack/utils/qemu/QemuImgTest.java | 20 +++++++------- 8 files changed, 65 insertions(+), 53 deletions(-) diff --git a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java index 629fafe545f..e9ec0b35f11 100644 --- a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -63,4 +63,8 @@ public final class CopyCommand extends Command implements StorageSubSystemComman this.cacheTO = cacheTO; } + public int getWaitInMillSeconds() { + return this.getWait() * 1000; + } + } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 04499d58851..e07d12233da 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1365,7 +1365,7 @@ ServerResource { secondaryStorageUrl + volumeDestPath); _storagePoolMgr.copyPhysicalDisk(volume, - destVolumeName,secondaryStoragePool); + destVolumeName,secondaryStoragePool, 0); return new CopyVolumeAnswer(cmd, true, null, null, volumeName); } else { volumePath = "/volumes/" + cmd.getVolumeId() + File.separator; @@ -1375,7 +1375,7 @@ ServerResource { KVMPhysicalDisk volume = secondaryStoragePool .getPhysicalDisk(cmd.getVolumePath() + ".qcow2"); _storagePoolMgr.copyPhysicalDisk(volume, volumeName, - primaryPool); + primaryPool, 0); return new CopyVolumeAnswer(cmd, true, null, null, volumeName); } } catch (CloudRuntimeException e) { @@ -1461,7 +1461,7 @@ ServerResource { } else { BaseVol = primaryPool.getPhysicalDisk(cmd.getTemplateUrl()); vol = _storagePoolMgr.createDiskFromTemplate(BaseVol, UUID - .randomUUID().toString(), primaryPool); + .randomUUID().toString(), primaryPool, 0); } if (vol == null) { return new Answer(cmd, false, @@ -1522,7 +1522,7 @@ ServerResource { /* Copy volume to primary storage */ - KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), primaryPool); + KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), primaryPool, 0); return primaryVol; } catch (CloudRuntimeException e) { s_logger.error("Failed to download template to primary storage",e); @@ -2347,7 +2347,7 @@ ServerResource { primaryUuid); String volUuid = UUID.randomUUID().toString(); KVMPhysicalDisk disk = _storagePoolMgr.copyPhysicalDisk(snapshot, - volUuid, primaryPool); + volUuid, primaryPool, 0); return new CreateVolumeFromSnapshotAnswer(cmd, true, "", disk.getName()); } catch (CloudRuntimeException e) { @@ -2498,7 +2498,7 @@ ServerResource { QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); destFile.setFormat(PhysicalDiskFormat.QCOW2); - QemuImg q = new QemuImg(); + QemuImg q = new QemuImg(0); try { q.convert(srcFile, destFile); } catch (QemuImgException e) { @@ -2601,7 +2601,7 @@ ServerResource { cmd.getPoolUuid()); KVMPhysicalDisk primaryVol = _storagePoolMgr.copyPhysicalDisk( - tmplVol, UUID.randomUUID().toString(), primaryPool); + tmplVol, UUID.randomUUID().toString(), primaryPool, 0); return new PrimaryStorageDownloadAnswer(primaryVol.getName(), primaryVol.getSize()); 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 e09c9ba44da..945243a9cf7 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 @@ -206,25 +206,25 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); // LibvirtStorageAdaptor-specific statement if (destPool.getType() == StoragePoolType.RBD) { return adaptor.createDiskFromTemplate(template, name, - PhysicalDiskFormat.RAW, template.getSize(), destPool); + PhysicalDiskFormat.RAW, template.getSize(), destPool, timeout); } else if (destPool.getType() == StoragePoolType.CLVM) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, template.getSize(), - destPool); + destPool, timeout); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, - template.getSize(), destPool); + template.getSize(), destPool, timeout); } else { return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, - template.getSize(), destPool); + template.getSize(), destPool, timeout); } } @@ -237,9 +237,9 @@ public class KVMStoragePoolManager { } public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); - return adaptor.copyPhysicalDisk(disk, name, destPool); + return adaptor.copyPhysicalDisk(disk, name, destPool, timeout); } public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index a74df193a08..82fd2ce99cc 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -194,7 +194,7 @@ public class KVMStorageProcessor implements StorageProcessor { primaryStore.getUuid()); KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(tmplVol, UUID.randomUUID().toString(), - primaryPool); + primaryPool, cmd.getWaitInMillSeconds()); DataTO data = null; @@ -232,7 +232,7 @@ public class KVMStorageProcessor implements StorageProcessor { } // this is much like PrimaryStorageDownloadCommand, but keeping it separate - private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool) { + private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool, int timeout) { int index = templateUrl.lastIndexOf("/"); String mountpoint = templateUrl.substring(0, index); String templateName = null; @@ -269,7 +269,7 @@ public class KVMStorageProcessor implements StorageProcessor { /* Copy volume to primary storage */ KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), - primaryPool); + primaryPool, timeout); return primaryVol; } catch (CloudRuntimeException e) { s_logger.error("Failed to download template to primary storage", e); @@ -300,14 +300,14 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { templatePath = ((NfsTO)imageStore).getUrl() + File.separator + templatePath; - vol = templateToPrimaryDownload(templatePath, primaryPool); + vol = templateToPrimaryDownload(templatePath, primaryPool, cmd.getWaitInMillSeconds()); } else { if (templatePath.contains("/mnt")) { //upgrade issue, if the path contains path, need to extract the volume uuid from path templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1); } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); - vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool(), cmd.getWaitInMillSeconds()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -378,7 +378,7 @@ public class KVMStorageProcessor implements StorageProcessor { .getPhysicalDisk(srcVolumeName); volume.setFormat(PhysicalDiskFormat.valueOf(srcFormat.toString())); KVMPhysicalDisk newDisk = storagePoolMgr.copyPhysicalDisk(volume, volumeName, - primaryPool); + primaryPool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setFormat(ImageFormat.valueOf(newDisk.getFormat().toString().toUpperCase())); newVol.setPath(volumeName); @@ -426,7 +426,7 @@ public class KVMStorageProcessor implements StorageProcessor { secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( secondaryStorageUrl + File.separator + destVolumePath); storagePoolMgr.copyPhysicalDisk(volume, - destVolumeName,secondaryStoragePool); + destVolumeName,secondaryStoragePool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(destVolumePath + File.separator + destVolumeName); newVol.setFormat(destFormat); @@ -444,7 +444,7 @@ public class KVMStorageProcessor implements StorageProcessor { public Answer createTemplateFromVolume(CopyCommand cmd) { DataTO srcData = cmd.getSrcTO(); DataTO destData = cmd.getDestTO(); - int wait = cmd.getWait(); + int wait = cmd.getWaitInMillSeconds(); TemplateObjectTO template = (TemplateObjectTO) destData; DataStoreTO imageStore = template.getDataStore(); VolumeObjectTO volume = (VolumeObjectTO) srcData; @@ -470,7 +470,7 @@ public class KVMStorageProcessor implements StorageProcessor { String templateName = UUID.randomUUID().toString(); if (primary.getType() != StoragePoolType.RBD) { - Script command = new Script(_createTmplPath, wait * 1000, s_logger); + Script command = new Script(_createTmplPath, wait, s_logger); command.add("-f", disk.getPath()); command.add("-t", tmpltPath); command.add("-n", templateName + ".qcow2"); @@ -491,7 +491,7 @@ public class KVMStorageProcessor implements StorageProcessor { QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + templateName + ".qcow2"); destFile.setFormat(PhysicalDiskFormat.QCOW2); - QemuImg q = new QemuImg(); + QemuImg q = new QemuImg(cmd.getWaitInMillSeconds()); try { q.convert(srcFile, destFile); } catch (QemuImgException e) { @@ -619,7 +619,7 @@ public class KVMStorageProcessor implements StorageProcessor { SnapshotObjectTO snapshotOnCacheStore = (SnapshotObjectTO)answer.getNewData(); snapshotOnCacheStore.setDataStore(cacheStore); ((SnapshotObjectTO) destData).setDataStore(imageStore); - CopyCommand newCpyCmd = new CopyCommand(snapshotOnCacheStore, destData, cmd.getWait(), cmd.executeInSequence()); + CopyCommand newCpyCmd = new CopyCommand(snapshotOnCacheStore, destData, cmd.getWaitInMillSeconds(), cmd.executeInSequence()); return copyToObjectStore(newCpyCmd); } @Override @@ -723,7 +723,7 @@ public class KVMStorageProcessor implements StorageProcessor { return new CopyCmdAnswer(e.toString()); } } else { - Script command = new Script(_manageSnapshotPath, cmd.getWait() * 1000, s_logger); + Script command = new Script(_manageSnapshotPath, cmd.getWaitInMillSeconds(), s_logger); command.add("-b", snapshotDisk.getPath()); command.add("-n", snapshotName); command.add("-p", snapshotDestPath); @@ -1185,7 +1185,7 @@ public class KVMStorageProcessor implements StorageProcessor { String primaryUuid = pool.getUuid(); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(pool.getPoolType(), primaryUuid); String volUuid = UUID.randomUUID().toString(); - KVMPhysicalDisk disk = storagePoolMgr.copyPhysicalDisk(snapshotDisk, volUuid, primaryPool); + KVMPhysicalDisk disk = storagePoolMgr.copyPhysicalDisk(snapshotDisk, volUuid, primaryPool, cmd.getWaitInMillSeconds()); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(disk.getName()); newVol.setSize(disk.getVirtualSize()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index b2bdd5b1d42..51e3363d2c8 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -766,7 +766,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { */ @Override public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, - String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { + String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { String newUuid = UUID.randomUUID().toString(); KVMStoragePool srcPool = template.getPool(); @@ -783,20 +783,20 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (destPool.getType() != StoragePoolType.RBD) { disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); if (template.getFormat() == PhysicalDiskFormat.TAR) { - Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath()); + Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath(), timeout); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { Script.runSimpleBashScript("mkdir -p " + disk.getPath()); Script.runSimpleBashScript("chmod 755 " + disk.getPath()); - Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath()); + Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath(), timeout); } else if (format == PhysicalDiskFormat.QCOW2) { QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); QemuImgFile destFile = new QemuImgFile(disk.getPath()); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); qemu.create(destFile, backingFile); } else if (format == PhysicalDiskFormat.RAW) { QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat()); QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); qemu.convert(sourceFile, destFile); } } else { @@ -806,7 +806,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { disk.setSize(template.getVirtualSize()); disk.setVirtualSize(disk.getSize()); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); QemuImgFile srcFile; QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), @@ -960,7 +960,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { */ @Override public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPool) { + KVMStoragePool destPool, int timeout) { /** With RBD you can't run qemu-img convert with an existing RBD image as destination @@ -999,24 +999,27 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { String destPath = newDisk.getPath(); PhysicalDiskFormat destFormat = newDisk.getFormat(); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(timeout); QemuImgFile srcFile = null; QemuImgFile destFile = null; if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) { if (sourceFormat == PhysicalDiskFormat.TAR) { - Script.runSimpleBashScript("tar -x -f " + sourcePath + " -C " + destPath); + Script.runSimpleBashScript("tar -x -f " + sourcePath + " -C " + destPath, timeout); } else if (sourceFormat == PhysicalDiskFormat.DIR) { Script.runSimpleBashScript("mkdir -p " + destPath); Script.runSimpleBashScript("chmod 755 " + destPath); - Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath); + Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath, timeout); } else { srcFile = new QemuImgFile(sourcePath, sourceFormat); try { Map info = qemu.info(srcFile); String backingFile = info.get(new String("backing_file")); if (sourceFormat.equals(destFormat) && backingFile == null) { - Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath); + String result = Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath, timeout); + if (result != null) { + throw new CloudRuntimeException("Failed to create disk: " + result); + } } else { destFile = new QemuImgFile(destPath, destFormat); try { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index 4956d8d4717..44e069116d6 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -40,7 +40,7 @@ public interface StorageAdaptor { public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, - KVMStoragePool destPool); + KVMStoragePool destPool, int timeout); public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, @@ -50,7 +50,7 @@ public interface StorageAdaptor { KVMStoragePool pool); public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, - KVMStoragePool destPools); + KVMStoragePool destPools, int timeout); public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool); diff --git a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java index 4ed85930e1f..0e83bc943e1 100644 --- a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -31,6 +31,7 @@ public class QemuImg { /* The qemu-img binary. We expect this to be in $PATH */ public String _qemuImgPath = "qemu-img"; + private int timeout; /* Shouldn't we have KVMPhysicalDisk and LibvirtVMDef read this? */ public static enum PhysicalDiskFormat { @@ -46,8 +47,12 @@ public class QemuImg { } } - public QemuImg() { + public QemuImg(int timeout) { + this.timeout = timeout; + } + public void setTimeout(int timeout) { + this.timeout = timeout; } /** @@ -84,7 +89,7 @@ public class QemuImg { * @return void */ public void create(QemuImgFile file, QemuImgFile backingFile, Map options) throws QemuImgException { - Script s = new Script(_qemuImgPath); + Script s = new Script(_qemuImgPath, timeout); s.add("create"); if (options != null && !options.isEmpty()) { @@ -181,7 +186,7 @@ public class QemuImg { * @return void */ public void convert(QemuImgFile srcFile, QemuImgFile destFile, Map options) throws QemuImgException { - Script s = new Script(_qemuImgPath); + Script s = new Script(_qemuImgPath, timeout); s.add("convert"); s.add("-f"); s.add(srcFile.getFormat().toString()); diff --git a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java index 9c6ac8b1e6a..5244dda9243 100644 --- a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java +++ b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java @@ -38,7 +38,7 @@ public class QemuImgTest { long size = 10995116277760l; QemuImgFile file = new QemuImgFile(filename, size, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); Map info = qemu.info(file); @@ -69,7 +69,7 @@ public class QemuImgTest { options.put("cluster_size", clusterSize); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file, options); Map info = qemu.info(file); @@ -96,7 +96,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, endSize); Map info = qemu.info(file); @@ -125,7 +125,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, increment, true); Map info = qemu.info(file); @@ -153,7 +153,7 @@ public class QemuImgTest { QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); try { - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, increment, true); Map info = qemu.info(file); @@ -182,7 +182,7 @@ public class QemuImgTest { long endSize = -1; QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); try { qemu.create(file); qemu.resize(file, endSize); @@ -199,7 +199,7 @@ public class QemuImgTest { long startSize = 20480; QemuImgFile file = new QemuImgFile(filename, 20480, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(file); qemu.resize(file, 0); @@ -216,7 +216,7 @@ public class QemuImgTest { QemuImgFile firstFile = new QemuImgFile(firstFileName, 20480, PhysicalDiskFormat.QCOW2); QemuImgFile secondFile = new QemuImgFile(secondFileName, PhysicalDiskFormat.QCOW2); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(firstFile); qemu.create(secondFile, firstFile); @@ -240,7 +240,7 @@ public class QemuImgTest { QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize); QemuImgFile destFile = new QemuImgFile(destFileName); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(srcFile); qemu.convert(srcFile, destFile); Map info = qemu.info(destFile); @@ -267,7 +267,7 @@ public class QemuImgTest { QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize, srcFormat); QemuImgFile destFile = new QemuImgFile(destFileName, destFormat); - QemuImg qemu = new QemuImg(); + QemuImg qemu = new QemuImg(0); qemu.create(srcFile); qemu.convert(srcFile, destFile); From 9baa45308ce12f17b71fc732916fb464ab13b5ba Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 12 Sep 2013 16:02:20 -0700 Subject: [PATCH 27/33] CLOUDSTACK-4650: change volume state during snapshot only --- .../snapshot/XenserverSnapshotStrategy.java | 21 +++++++++++++++---- .../storage/volume/VolumeServiceImpl.java | 11 ---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index fbf908184dc..60d9407f2a0 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.storage.snapshot; import javax.inject.Inject; +import com.cloud.storage.Volume; import com.cloud.utils.db.DB; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; @@ -244,11 +245,23 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { } try { - SnapshotResult result = snapshotSvr.takeSnapshot(snapshot); - if (result.isFailed()) { - s_logger.debug("Failed to take snapshot: " + result.getResult()); - throw new CloudRuntimeException(result.getResult()); + VolumeInfo volumeInfo = snapshot.getBaseVolume(); + volumeInfo.stateTransit(Volume.Event.SnapshotRequested); + SnapshotResult result = null; + try { + result = snapshotSvr.takeSnapshot(snapshot); + if (result.isFailed()) { + s_logger.debug("Failed to take snapshot: " + result.getResult()); + throw new CloudRuntimeException(result.getResult()); + } + } finally { + if (result != null && result.isSuccess()) { + volumeInfo.stateTransit(Volume.Event.OperationSucceeded); + } else { + volumeInfo.stateTransit(Volume.Event.OperationFailed); + } } + snapshot = result.getSnashot(); DataStore primaryStore = snapshot.getDataStore(); diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 28142ea701c..340d7037c19 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -1292,22 +1292,11 @@ public class VolumeServiceImpl implements VolumeService { @Override public SnapshotInfo takeSnapshot(VolumeInfo volume) { - VolumeObject vol = (VolumeObject) volume; - boolean result = vol.stateTransit(Volume.Event.SnapshotRequested); - if (!result) { - s_logger.debug("Failed to transit state"); - } SnapshotInfo snapshot = null; try { snapshot = snapshotMgr.takeSnapshot(volume); } catch (Exception e) { s_logger.debug("Take snapshot: " + volume.getId() + " failed", e); - } finally { - if (snapshot != null) { - vol.stateTransit(Volume.Event.OperationSucceeded); - } else { - vol.stateTransit(Volume.Event.OperationFailed); - } } return snapshot; From a9aad4f3887e91061b569e9aebd75d0d6a16073e Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 16:35:31 -0700 Subject: [PATCH 28/33] add sourcetemplateid for template created from volume/snapshot Conflicts: server/src/com/cloud/template/TemplateManagerImpl.java --- .../src/com/cloud/template/TemplateManagerImpl.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index c52c323cb58..9fa2e8bf2f2 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1389,16 +1389,23 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, //getting the prent volume long parentVolumeId=_snapshotDao.findById(snapshotId).getVolumeId(); VolumeVO parentVolume = _volumeDao.findById(parentVolumeId); - if (parentVolume.getIsoId() != null) { + + if (parentVolume != null && parentVolume.getIsoId() != null && parentVolume.getIsoId() != 0) { privateTemplate.setSourceTemplateId(parentVolume.getIsoId()); _tmpltDao.update(privateTemplate.getId(), privateTemplate); + } else if (parentVolume != null && parentVolume.getTemplateId() != null) { + privateTemplate.setSourceTemplateId(parentVolume.getTemplateId()); + _tmpltDao.update(privateTemplate.getId(), privateTemplate); } } else if (volumeId != null) { VolumeVO parentVolume = _volumeDao.findById(volumeId); - if (parentVolume.getIsoId() != null) { + if (parentVolume.getIsoId() != null && parentVolume.getIsoId() != 0) { privateTemplate.setSourceTemplateId(parentVolume.getIsoId()); _tmpltDao.update(privateTemplate.getId(), privateTemplate); + } else if (parentVolume.getTemplateId() != null) { + privateTemplate.setSourceTemplateId(parentVolume.getTemplateId()); + _tmpltDao.update(privateTemplate.getId(), privateTemplate); } } TemplateDataStoreVO srcTmpltStore = _tmplStoreDao.findByStoreTemplate(store.getId(), templateId); From 81ff4795df4f58b9ddc0c3d03ef833c756fd5b54 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Wed, 25 Sep 2013 17:59:54 -0700 Subject: [PATCH 29/33] fix compile errors --- .../src/com/cloud/vm/VirtualMachineManagerImpl.java | 6 ------ .../driver/CloudStackPrimaryDataStoreDriverImpl.java | 2 +- server/src/com/cloud/user/AccountManagerImpl.java | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 3829336d28b..af6ca6d643d 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1203,14 +1203,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } catch (AgentUnavailableException e) { s_logger.warn("Unable to stop vm, agent unavailable: " + e.toString()); - if (!forced) { - throw e; - } } catch (OperationTimedoutException e) { s_logger.warn("Unable to stop vm, operation timed out: " + e.toString()); - if (!forced) { - throw e; - } } finally { if (!stopped) { if (!cleanUpEvenIfUnableToStop) { diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 96d3ac0d3cf..3eaeb1f080c 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -26,7 +26,6 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.configuration.Config; -import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.dao.HostDao; import com.cloud.storage.DataStoreRole; @@ -44,6 +43,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.vm.dao.VMInstanceDao; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 9a035903fbc..396319af538 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -857,7 +857,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M @DB @ActionEvents({ @ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_CREATE, eventDescription = "creating Account"), - @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User"), + @ActionEvent(eventType = EventTypes.EVENT_USER_CREATE, eventDescription = "creating User") }) public UserAccount createUserAccount(String userName, String password, String firstName, String lastName, String email, String timezone, String accountName, short accountType, Long domainId, String networkDomain, Map details, String accountUUID, String userUUID) { From 7cb5a191cf01dc486b692904d2d81f5a171d9afb Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Thu, 26 Sep 2013 07:31:15 +0200 Subject: [PATCH 30/33] ui: RBD doesn't work with LXC, remove it from the UI --- ui/scripts/system.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index ad2c9955a6d..7bdd5390ab3 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -12803,10 +12803,6 @@ id: "SharedMountPoint", description: "SharedMountPoint" }); - items.push({ - id: "rbd", - description: "RBD" - }); args.response.success({ data: items }); From b9c13d0e738a38732d50449eb646d294c326aeb1 Mon Sep 17 00:00:00 2001 From: Wido den Hollander Date: Thu, 26 Sep 2013 09:47:59 +0200 Subject: [PATCH 31/33] rbd: Add more help information to the UI when adding a Primary Pool. --- .../classes/resources/messages.properties | 7 ++++++- ui/dictionary.jsp | 4 ++++ ui/scripts/docs.js | 16 ++++++++++++++++ ui/scripts/system.js | 4 ++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index bc1e43692a3..48c6b397b1f 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -39,7 +39,7 @@ message.acquire.ip.nic=Please confirm that you would like to acquire a new secon message.select.affinity.groups=Please select any affinity groups you want this VM to belong to: message.no.affinity.groups=You do not have any affinity groups. Please continue to the next step. label.action.delete.nic=Remove NIC -message.action.delete.nic=Please confirm that want to remove this NIC, which will also remove the associated network from the VM. +message.action.delete.nic=Please confirm that want to remove this NIC, which will also remove the associated network from the VM. changed.item.properties=Changed item properties confirm.enable.s3=Please fill in the following information to enable support for S3-backed Secondary Storage confirm.enable.swift=Please fill in the following information to enable support for Swift @@ -412,6 +412,11 @@ label.cluster.type=Cluster Type label.cluster=Cluster label.clusters=Clusters label.clvm=CLVM +label.rbd=RBD +label.rbd.monitor=Ceph monitor +label.rbd.pool=Ceph pool +label.rbd.id=Cephx user +label.rbd.secret=Cephx secret label.code=Code label.community=Community label.compute.and.storage=Compute and Storage diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 15c199964b3..f2505d32a39 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -903,6 +903,10 @@ dictionary = { 'label.Pxe.server.type': '', 'label.quickview': '', 'label.rbd': '', +'label.rbd.monitor': '', +'label.rbd.pool': '', +'label.rbd.id': '', +'label.rbd.secret': '', 'label.reboot': '', 'label.recent.errors': '', 'label.redundant.router.capability': '', diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index 1431c1b7b1f..3a4f8ca604f 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -643,6 +643,22 @@ cloudStack.docs = { desc: 'In iSCSI, this is the LUN number. For example, 3.', externalLink: '' }, + helpPrimaryStorageRBDMonitor: { + desc: 'The address of a Ceph monitor. Can also be a Round Robin DNS record', + externalLink: '' + }, + helpPrimaryStorageRBDPool: { + desc: 'The pool to use on the Ceph cluster. This pool should already exist', + externalLink: '' + }, + helpPrimaryStorageRBDId: { + desc: 'The cephx user to use without the client. prefix. For example: admin', + externalLink: '' + }, + helpPrimaryStorageRBDSecret: { + desc: 'The base64 encoded secret of the cephx user.', + externalLink: '' + }, helpPrimaryStorageTags: { desc: 'Comma-separated list of tags for this storage device. Must be the same set or a superset of the tags on your disk offerings.', externalLink: '' diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7bdd5390ab3..f5ed9a20c43 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -13113,6 +13113,7 @@ // RBD rbdmonitor: { label: 'label.rbd.monitor', + docID: 'helpPrimaryStorageRBDMonitor', validation: { required: true }, @@ -13120,6 +13121,7 @@ }, rbdpool: { label: 'label.rbd.pool', + docID: 'helpPrimaryStorageRBDPool', validation: { required: true }, @@ -13127,6 +13129,7 @@ }, rbdid: { label: 'label.rbd.id', + docID: 'helpPrimaryStorageRBDId', validation: { required: false }, @@ -13134,6 +13137,7 @@ }, rbdsecret: { label: 'label.rbd.secret', + docID: 'helpPrimaryStorageRBDSecret', validation: { required: false }, From 6f970c6ff91cd41dbfb208cedb8fe333279d38a9 Mon Sep 17 00:00:00 2001 From: Daan Hoogland Date: Thu, 26 Sep 2013 15:18:52 +0200 Subject: [PATCH 32/33] static nat capabilities parsing cleanup and testing --- .../ConfigurationManagerImpl.java | 35 +++------- .../ConfigurationManagerTest.java | 68 +++++++++++++++++++ 2 files changed, 78 insertions(+), 25 deletions(-) diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index f23d57d84bf..8a0f7a65a12 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3924,42 +3924,27 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati void validateStaticNatServiceCapablities(Map staticNatServiceCapabilityMap) { if (staticNatServiceCapabilityMap != null && !staticNatServiceCapabilityMap.isEmpty()) { - if (staticNatServiceCapabilityMap.keySet().size() > 2) { - throw new InvalidParameterValueException("Only " + Capability.ElasticIp.getName() + " and " - + Capability.AssociatePublicIP.getName() - + " capabilitiy can be sepcified for static nat service"); - } - boolean eipEnabled = false; - boolean eipDisabled = false; boolean associatePublicIP = true; for (Capability capability : staticNatServiceCapabilityMap.keySet()) { - String value = staticNatServiceCapabilityMap.get(capability); + String value = staticNatServiceCapabilityMap.get(capability).toLowerCase(); + if (! (value.contains("true") ^ value.contains("false"))) { + throw new InvalidParameterValueException("Unknown specified value (" + value + ") for " + + capability); + } if (capability == Capability.ElasticIp) { eipEnabled = value.contains("true"); - eipDisabled = value.contains("false"); - if (!eipEnabled && !eipDisabled) { - throw new InvalidParameterValueException("Unknown specified value for " - + Capability.ElasticIp.getName()); - } } else if (capability == Capability.AssociatePublicIP) { - if (value.contains("true")) { - associatePublicIP = true; - } else if (value.contains("false")) { - associatePublicIP = false; - } else { - throw new InvalidParameterValueException("Unknown specified value for " - + Capability.AssociatePublicIP.getName()); - } + associatePublicIP = value.contains("true"); } else { throw new InvalidParameterValueException("Only " + Capability.ElasticIp.getName() + " and " + Capability.AssociatePublicIP.getName() + " capabilitiy can be sepcified for static nat service"); } - if (eipDisabled && associatePublicIP) { - throw new InvalidParameterValueException("Capability " + Capability.AssociatePublicIP.getName() - + " can only be set when capability " + Capability.ElasticIp.getName() + " is true"); - } + } + if ((! eipEnabled) && associatePublicIP) { + throw new InvalidParameterValueException("Capability " + Capability.AssociatePublicIP.getName() + + " can only be set when capability " + Capability.ElasticIp.getName() + " is true"); } } } diff --git a/server/test/com/cloud/configuration/ConfigurationManagerTest.java b/server/test/com/cloud/configuration/ConfigurationManagerTest.java index e49d93adb47..908f0d01056 100755 --- a/server/test/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/test/com/cloud/configuration/ConfigurationManagerTest.java @@ -27,7 +27,9 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import junit.framework.Assert; @@ -53,7 +55,9 @@ import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.VlanDao; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.IpAddressManager; +import com.cloud.network.Network.Capability; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; @@ -415,6 +419,70 @@ public class ConfigurationManagerTest { } } + @Test + public void validateEmptyStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } + + @Test + public void validateInvalidStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "Frue and Talse"); + + boolean caught = false; + try { + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } + catch (InvalidParameterValueException e) { + Assert.assertTrue(e.getMessage(),e.getMessage().contains("(frue and talse)")); + caught = true; + } + Assert.assertTrue("should not be accepted",caught); + } + + @Test + public void validateTTStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse"); + staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True"); + + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } + @Test + public void validateFTStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false"); + staticNatServiceCapabilityMap.put(Capability.ElasticIp, "True"); + + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } + @Test + public void validateTFStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "true and Talse"); + staticNatServiceCapabilityMap.put(Capability.ElasticIp, "false"); + + boolean caught = false; + try { + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } + catch (InvalidParameterValueException e) { + Assert.assertTrue(e.getMessage(),e.getMessage().contains("Capability " + Capability.AssociatePublicIP.getName() + + " can only be set when capability " + Capability.ElasticIp.getName() + " is true")); + caught = true; + } + Assert.assertTrue("should not be accepted",caught); + } + @Test + public void validateFFStaticNatServiceCapablitiesTest() { + Map staticNatServiceCapabilityMap = new HashMap(); + staticNatServiceCapabilityMap.put(Capability.AssociatePublicIP, "false"); + staticNatServiceCapabilityMap.put(Capability.ElasticIp, "False"); + + configurationMgr.validateStaticNatServiceCapablities(staticNatServiceCapabilityMap); + } public class DedicatePublicIpRangeCmdExtn extends DedicatePublicIpRangeCmd { @Override From a45ee749ace84a88e8f0b4c50ade59f71d45e89e Mon Sep 17 00:00:00 2001 From: ynojima Date: Mon, 23 Sep 2013 21:37:38 -0600 Subject: [PATCH 33/33] CLOUDSTACK-2328: Linux native VXLAN support on KVM hypervisor Initial patch for VXLAN support. Fully functional, hopefully, for GuestNetwork - AdvancedZone. Patch Note: in cloudstack-server - Add isolation method VXLAN - Add VxlanGuestNetworkGuru as plugin for VXLAN isolation - Modify NetworkServiceImpl to handle extended vNet range for VXLAN isolation - Add VXLAN isolation option in zoneWizard UI in cloudstack-agent (kvm) - Add modifyvxlan.sh script that handle bridge/vxlan interface manipulation script -- Usage is exactly same to modifyvlan.sh - BridgeVifDriver will call modifyvxlan.sh instead of modifyvlan.sh when VXLAN is used for isolation Database changes: - No change in database structure. - VXLAN isolation uses same tables that VLAN uses to store vNet allocation status. Known Issue and/or TODO: - Some resource still says 'VLAN' in log even if VXLAN is used - in UI, "Network - GuestNetworks" dosen't display VNI -- VLAN ID field displays "N/A" - Documentation! Signed-off-by : Toshiaki Hatano --- api/src/com/cloud/network/Networks.java | 1 + .../com/cloud/network/PhysicalNetwork.java | 3 +- .../classes/resources/messages.properties | 7 + .../resources/messages_de_DE.properties | 4 + .../classes/resources/messages_es.properties | 5 + .../resources/messages_fr_FR.properties | 7 + .../classes/resources/messages_ja.properties | 9 +- .../resources/messages_ko_KR.properties | 7 + .../resources/messages_pt_BR.properties | 7 + .../resources/messages_ru_RU.properties | 7 + .../resources/messages_zh_CN.properties | 7 + client/pom.xml | 5 + client/tomcatconf/componentContext.xml.in | 8 + .../orchestration/NetworkOrchestrator.java | 8 +- .../kvm/resource/BridgeVifDriver.java | 68 +++-- plugins/network-elements/vxlan/pom.xml | 29 ++ .../network/guru/VxlanGuestNetworkGuru.java | 179 ++++++++++++ .../guru/VxlanGuestNetworkGuruTest.java | 274 ++++++++++++++++++ plugins/pom.xml | 1 + scripts/vm/network/vnet/modifyvxlan.sh | 230 +++++++++++++++ .../com/cloud/network/NetworkServiceImpl.java | 5 + ui/dictionary.jsp | 7 + ui/scripts/sharedFunctions.js | 5 +- ui/scripts/ui-custom/zoneWizard.js | 6 +- 24 files changed, 859 insertions(+), 30 deletions(-) create mode 100644 plugins/network-elements/vxlan/pom.xml create mode 100644 plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java create mode 100644 plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java create mode 100755 scripts/vm/network/vnet/modifyvxlan.sh diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java index 7069282a669..0412bf45982 100755 --- a/api/src/com/cloud/network/Networks.java +++ b/api/src/com/cloud/network/Networks.java @@ -108,6 +108,7 @@ public class Networks { }, Mido("mido", String.class), Pvlan("pvlan", String.class), + Vxlan("vxlan", Long.class), UnDecided(null, null); private final String scheme; diff --git a/api/src/com/cloud/network/PhysicalNetwork.java b/api/src/com/cloud/network/PhysicalNetwork.java index f6cb1a6e0b6..55b18e67ba9 100644 --- a/api/src/com/cloud/network/PhysicalNetwork.java +++ b/api/src/com/cloud/network/PhysicalNetwork.java @@ -39,7 +39,8 @@ public interface PhysicalNetwork extends Identity, InternalIdentity { STT, VNS, MIDO, - SSP; + SSP, + VXLAN; } public enum BroadcastDomainRange { diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 48c6b397b1f..f92b85aa944 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -315,6 +315,7 @@ label.add.template=Add Template label.add.to.group=Add to group label.add.user=Add User label.add.vlan=Add VLAN +label.add.vxlan=Add VXLAN label.add.VM.to.tier=Add VM to tier label.add.vm=Add VM label.add.vms.to.lb=Add VM(s) to load balancer rule @@ -545,6 +546,7 @@ label.end.IP=End IP label.end.port=End Port label.end.reserved.system.IP=End Reserved system IP label.end.vlan=End Vlan +label.end.vxlan=End Vxlan label.endpoint.or.operation=Endpoint or Operation label.endpoint=Endpoint label.enter.token=Enter token @@ -1031,12 +1033,14 @@ label.source.nat=Source NAT label.source=Source label.specify.IP.ranges=Specify IP ranges label.specify.vlan=Specify VLAN +label.specify.vxlan=Specify VXLAN label.SR.name = SR Name-Label label.srx=SRX label.start.IP=Start IP label.start.port=Start Port label.start.reserved.system.IP=Start Reserved system IP label.start.vlan=Start Vlan +label.start.vxlan=Start Vxlan label.state=State label.static.nat.enabled=Static NAT Enabled label.static.nat.to=Static NAT to @@ -1163,6 +1167,9 @@ label.virtual.routers=Virtual Routers label.vlan.id=VLAN ID label.vlan.range=VLAN Range label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN Range +label.vxlan=VXLAN label.vm.add=Add Instance label.vm.destroy=Destroy label.vm.display.name=VM display name diff --git a/client/WEB-INF/classes/resources/messages_de_DE.properties b/client/WEB-INF/classes/resources/messages_de_DE.properties index 3c0c8deaabd..2f164609d00 100644 --- a/client/WEB-INF/classes/resources/messages_de_DE.properties +++ b/client/WEB-INF/classes/resources/messages_de_DE.properties @@ -224,6 +224,7 @@ label.add.system.service.offering=System-Service-Angebot hinzuf\u00fcgen label.add.template=Vorlage hinzuf\u00fcgen label.add.user=Benutzer hinzuf\u00fcgen label.add.vlan=VLAN hinzuf\u00fcgen +label.add.vxlan=VXLAN hinzuf\u00fcgen label.add.volume=Volume hinzuf\u00fcgen label.add.zone=Zone hinzuf\u00fcgen label.admin.accounts=Administrator-Konten @@ -621,6 +622,9 @@ label.virtual.network=Virtuelles Netzwerk label.vlan.id=VLAN ID label.vlan.range=VLAN Reichweite label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN Reichweite +label.vxlan=VXLAN label.vm.add=Instanz hinzuf\u00fcgen label.vm.destroy=Zerst\u00f6ren label.VMFS.datastore=VMFS Datenspeicher diff --git a/client/WEB-INF/classes/resources/messages_es.properties b/client/WEB-INF/classes/resources/messages_es.properties index 86eb596689c..3620047a275 100644 --- a/client/WEB-INF/classes/resources/messages_es.properties +++ b/client/WEB-INF/classes/resources/messages_es.properties @@ -238,6 +238,7 @@ label.add.template=A\u00c3\u00b1adir plantilla label.add.to.group=Agregar al grupo label.add.user=Agregar usuario label.add.vlan=A\u00c3\u00b1adir VLAN +label.add.vxlan=A\u00c3\u00b1adir VXLAN label.add.volume=A\u00c3\u00b1adir volumen label.add.zone=A\u00c3\u00b1adir Zona label.admin.accounts=Administrador de Cuentas @@ -606,6 +607,7 @@ label.snapshot.s=Instant\u00c3\u00a1nea (s) label.snapshots=instant\u00c3\u00a1neas label.source.nat=NAT Fuente label.specify.vlan=Especifique VLAN +label.specify.vxlan=Especifique VXLAN label.SR.name = SR Nombre de etiqueta label.start.port=Iniciar Puerto label.state=Estado @@ -685,6 +687,9 @@ label.virtual.network=Red Virtual label.vlan.id=ID de VLAN label.vlan.range=VLAN Gama label.vlan=VLAN +label.vxlan.id=ID de VXLAN +label.vxlan.range=VXLAN Gama +label.vxlan=VXLAN label.vm.add=A\u00c3\u00b1adir Instancia label.vm.destroy=Destroy label.VMFS.datastore=VMFS de datos tienda diff --git a/client/WEB-INF/classes/resources/messages_fr_FR.properties b/client/WEB-INF/classes/resources/messages_fr_FR.properties index 284fde89386..db624221ddf 100644 --- a/client/WEB-INF/classes/resources/messages_fr_FR.properties +++ b/client/WEB-INF/classes/resources/messages_fr_FR.properties @@ -300,6 +300,7 @@ label.add.template=Ajouter un mod\u00e8le label.add.to.group=Ajouter au groupe label.add.user=Ajouter un utilisateur label.add.vlan=Ajouter un VLAN +label.add.vxlan=Ajouter un VXLAN label.add.vm=Ajouter VM label.add.vms=Ajouter VMs label.add.vms.to.lb=Ajouter une/des VM(s) \u00e0 la r\u00e8gle de r\u00e9partition de charge @@ -512,6 +513,7 @@ label.endpoint=Terminaison label.end.port=Port de fin label.end.reserved.system.IP=Adresse IP de fin r\u00e9serv\u00e9e Syst\u00e8me label.end.vlan=VLAN de fin +label.end.vxlan=VXLAN de fin label.enter.token=Entrez le jeton unique label.error.code=Code d\\'erreur label.error=Erreur @@ -995,12 +997,14 @@ label.source.nat=NAT Source label.source=Origine label.specify.IP.ranges=Sp\u00e9cifier des plages IP label.specify.vlan=Pr\u00e9ciser le VLAN +label.specify.vxlan=Pr\u00e9ciser le VXLAN label.SR.name = Nom du point de montage label.srx=SRX label.start.IP=Plage de d\u00e9but IP label.start.port=Port de d\u00e9but label.start.reserved.system.IP=Adresse IP de d\u00e9but r\u00e9serv\u00e9e Syst\u00e8me label.start.vlan=VLAN de d\u00e9part +label.start.vxlan=VXLAN de d\u00e9part label.state=\u00c9tat label.static.nat.enabled=NAT statique activ\u00e9 label.static.nat=NAT Statique @@ -1127,6 +1131,9 @@ label.virtual.routers=Routeurs virtuels label.vlan.id=ID du VLAN label.vlan.range=Plage du VLAN label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=Plage du VXLAN +label.vxlan=VXLAN label.vm.add=Ajouter une instance label.vm.destroy=D\u00e9truire label.vm.display.name=Nom commun VM diff --git a/client/WEB-INF/classes/resources/messages_ja.properties b/client/WEB-INF/classes/resources/messages_ja.properties index 56fa55a3e4c..d01efe88ff1 100644 --- a/client/WEB-INF/classes/resources/messages_ja.properties +++ b/client/WEB-INF/classes/resources/messages_ja.properties @@ -1,4 +1,4 @@ -# Licensed to the Apache Software Foundation (ASF) under one +a# 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 @@ -313,6 +313,7 @@ label.add.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u8ffd\u52a0 label.add.to.group=\u8ffd\u52a0\u5148\u30b0\u30eb\u30fc\u30d7 label.add.user=\u30e6\u30fc\u30b6\u30fc\u306e\u8ffd\u52a0 label.add.vlan=VLAN \u306e\u8ffd\u52a0 +label.add.vxlan=VXLAN \u306e\u8ffd\u52a0 label.add.VM.to.tier=\u968e\u5c64\u3078\u306e VM \u306e\u8ffd\u52a0 label.add.vm=VM \u306e\u8ffd\u52a0 label.add.vms.to.lb=\u8ca0\u8377\u5206\u6563\u898f\u5247\u3078\u306e VM \u306e\u8ffd\u52a0 @@ -532,6 +533,7 @@ label.end.IP=\u7d42\u4e86 IP \u30a2\u30c9\u30ec\u30b9 label.end.port=\u7d42\u4e86\u30dd\u30fc\u30c8 label.end.reserved.system.IP=\u4e88\u7d04\u6e08\u307f\u7d42\u4e86\u30b7\u30b9\u30c6\u30e0 IP \u30a2\u30c9\u30ec\u30b9 label.end.vlan=\u7d42\u4e86 VLAN +label.end.vxlan=\u7d42\u4e86 VXLAN label.endpoint.or.operation=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u307e\u305f\u306f\u64cd\u4f5c label.endpoint=\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8 label.enter.token=\u30c8\u30fc\u30af\u30f3\u306e\u5165\u529b @@ -1007,12 +1009,14 @@ label.source.nat=\u9001\u4fe1\u5143 NAT label.source=\u9001\u4fe1\u5143 label.specify.IP.ranges=IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u306e\u6307\u5b9a label.specify.vlan=VLAN \u3092\u6307\u5b9a\u3059\u308b +label.specify.vxlan=VXLAN \u3092\u6307\u5b9a\u3059\u308b label.SR.name = SR \u540d\u30e9\u30d9\u30eb label.srx=SRX label.start.IP=\u958b\u59cb IP \u30a2\u30c9\u30ec\u30b9 label.start.port=\u958b\u59cb\u30dd\u30fc\u30c8 label.start.reserved.system.IP=\u4e88\u7d04\u6e08\u307f\u958b\u59cb\u30b7\u30b9\u30c6\u30e0 IP \u30a2\u30c9\u30ec\u30b9 label.start.vlan=\u958b\u59cb VLAN +label.start.vxlan=\u958b\u59cb VXLAN label.state=\u72b6\u614b label.static.nat.enabled=\u9759\u7684 NAT \u6709\u52b9 label.static.nat.to=\u9759\u7684 NAT \u306e\u8a2d\u5b9a\u5148: @@ -1139,6 +1143,9 @@ label.virtual.routers=\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc label.vlan.id=VLAN ID label.vlan.range=VLAN \u306e\u7bc4\u56f2 label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN \u306e\u7bc4\u56f2 +label.vxlan=VXLAN label.vm.add=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u8ffd\u52a0 label.vm.destroy=\u7834\u68c4 label.vm.display.name=VM \u8868\u793a\u540d diff --git a/client/WEB-INF/classes/resources/messages_ko_KR.properties b/client/WEB-INF/classes/resources/messages_ko_KR.properties index 7f3d5ebae75..b755072d613 100644 --- a/client/WEB-INF/classes/resources/messages_ko_KR.properties +++ b/client/WEB-INF/classes/resources/messages_ko_KR.properties @@ -289,6 +289,7 @@ label.add.to.group=\uadf8\ub8f9\uc5d0 \ucd94\uac00 label.add=\ucd94\uac00 label.add.user=\uc0ac\uc6a9\uc790 \ucd94\uac00 label.add.vlan=VLAN \ucd94\uac00 +label.add.vxlan=VXLAN \ucd94\uac00 label.add.vms.to.lb=\ub124\ud2b8\uc6cc\ud06c \ub85c\ub4dc \uacf5\uc720 \uaddc\uce59\uc5d0 VM \ucd94\uac00 label.add.vms=VM \ucd94\uac00 label.add.VM.to.tier=\uacc4\uce35\uc5d0 VM \ucd94\uac00 @@ -479,6 +480,7 @@ label.endpoint.or.operation=\uc5d4\ub4dc \ud3ec\uc778\ud2b8 \ub610\ub294 \uc791\ label.end.port=\uc885\ub8cc \ud3ec\ud1a0 label.end.reserved.system.IP=\uc608\uc57d\ub41c \uc885\ub8cc \uc2dc\uc2a4\ud15c IP \uc8fc\uc18c label.end.vlan=\uc885\ub8cc VLAN +label.end.vxlan=\uc885\ub8cc VXLAN label.enter.token=\ud1a0\ud070 \uc785\ub825 label.error.code=\uc624\ub958 \ucf54\ub4dc label.error=\uc624\ub958 @@ -925,12 +927,14 @@ label.source.nat=\uc804\uc1a1\uc6d0 NAT label.source=\uc2dc\uc791 \uc704\uce58 label.specify.IP.ranges=IP \uc8fc\uc18c \ubc94\uc704 \uc9c0\uc815 label.specify.vlan=VLAN \uc9c0\uc815 +label.specify.vxlan=VXLAN \uc9c0\uc815 label.SR.name = SR \uba85 \ub77c\ubca8 label.srx=SRX label.start.IP=\uc2dc\uc791 IP \uc8fc\uc18c label.start.port=\uc2dc\uc791 \ud3ec\ud1a0 label.start.reserved.system.IP=\uc608\uc57d\ub41c \uc2dc\uc791 \uc2dc\uc2a4\ud15c IP \uc8fc\uc18c label.start.vlan=\uc2dc\uc791 VLAN +label.start.vxlan=\uc2dc\uc791 VXLAN label.state=\uc0c1\ud0dc label.static.nat.enabled=\uc815\uc801 NAT \uc720\ud6a8 label.static.nat.to=\uc815\uc801 NAT \uc124\uc815 \uc704\uce58\: @@ -1055,6 +1059,9 @@ label.virtual.router=\uac00\uc0c1 \ub77c\uc6b0\ud130 label.vlan.id=VLAN ID label.vlan.range=VLAN \ubc94\uc704 label.vlan=\uac00\uc0c1 \ub124\ud2b8\uc6cc\ud06c(VLAN) +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN \ubc94\uc704 +label.vxlan=VXLAN label.vm.add=\uc778\uc2a4\ud134\uc2a4 \ucd94\uac00 label.vm.destroy=\ud30c\uae30 label.vm.display.name=VM \ud45c\uc2dc\uba85 diff --git a/client/WEB-INF/classes/resources/messages_pt_BR.properties b/client/WEB-INF/classes/resources/messages_pt_BR.properties index 9f7a663657f..86bb83177a8 100644 --- a/client/WEB-INF/classes/resources/messages_pt_BR.properties +++ b/client/WEB-INF/classes/resources/messages_pt_BR.properties @@ -288,6 +288,7 @@ label.add.template=Adicionar Template label.add.to.group=Adicionar ao grupo label.add.user=Adicionar Usu\u00e1rio label.add.vlan=Adicionar VLAN +label.add.vxlan=Adicionar VXLAN label.add.vm=Adicionar VM label.add.vms=Adicionar VMs label.add.vms.to.lb=Add VM(s) na regra de balanceamento de carga @@ -480,6 +481,7 @@ label.endpoint=Ponto de acesso label.end.port=Porta Final label.end.reserved.system.IP=Fim dos IPs reservados para o sistema label.end.vlan=Vlan do fim +label.end.vxlan=Vxlan do fim label.enter.token=Digite o token label.error.code=C\u00f3digo de Erro label.error=Erro @@ -931,12 +933,14 @@ label.source.nat=Source NAT label.source=Origem label.specify.IP.ranges=Especifique range de IP label.specify.vlan=Especificar VLAN +label.specify.vxlan=Especificar VXLAN label.SR.name = SR Name-Label label.srx=SRX label.start.IP=IP do in\u00edcio label.start.port=Porta de In\u00edcio label.start.reserved.system.IP=In\u00edcio dos IPs reservados para o sistema label.start.vlan=Vlan do in\u00edcio +label.start.vxlan=Vxlan do in\u00edcio label.state=Estado label.static.nat.enabled=NAT est\u00e1tico Habilitado label.static.nat=NAT Est\u00e1tico @@ -1059,6 +1063,9 @@ label.virtual.routers=Roteadores Virtuais label.vlan.id=VLAN ID label.vlan.range=Intervalo de VLAN label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=Intervalo de VXLAN +label.vxlan=VXLAN label.vm.add=Adicionar Cloud Server label.vm.destroy=Apagar label.vm.display.name=Nome de exibi\u00e7\u00e3o da VM diff --git a/client/WEB-INF/classes/resources/messages_ru_RU.properties b/client/WEB-INF/classes/resources/messages_ru_RU.properties index 37a36a9b022..62c791f61b9 100644 --- a/client/WEB-INF/classes/resources/messages_ru_RU.properties +++ b/client/WEB-INF/classes/resources/messages_ru_RU.properties @@ -283,6 +283,7 @@ label.add.to.group=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043 label.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c label.add.user=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f label.add.vlan=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c VLAN +label.add.vxlan=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c VXLAN label.add.vms.to.lb=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0412\u041c \u0432 \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u043a\u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 label.add.vms=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0412\u041c label.add.vm=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0412\u041c @@ -454,6 +455,7 @@ label.endpoint.or.operation=\u041a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0 label.end.port=\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 label.end.reserved.system.IP=\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 label.end.vlan=\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 VLAN +label.end.vxlan=\u041a\u043e\u043d\u0435\u0447\u043d\u044b\u0439 VXLAN label.enter.token=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0430\u043b\u043e\u043d label.error.code=\u041a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438 label.error=\u041e\u0448\u0438\u0431\u043a\u0430 @@ -874,12 +876,14 @@ label.source.nat=Source NAT label.source=\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a label.specify.IP.ranges=\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d IP-\u0430\u0434\u0440\u0435\u0441\u043e\u0432 label.specify.vlan=\u0423\u043a\u0430\u0436\u0438\u0442\u0435 VLAN +label.specify.vxlan=\u0423\u043a\u0430\u0436\u0438\u0442\u0435 VXLAN label.SR.name = SR Name-Label label.srx=SRX label.start.IP=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 IP label.start.port=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0440\u0442 label.start.reserved.system.IP=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441 label.start.vlan=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 VLAN +label.start.vxlan=\u041d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0439 VXLAN label.state=\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 label.static.nat.enabled=\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 NAT \u0432\u043a\u043b\u044e\u0447\u0435\u043d label.static.nat.to=\u0421\u0442\u0430\u0442\u0438\u0447\u043d\u044b\u0439 NAT \u043a @@ -1001,6 +1005,9 @@ label.virtual.router=\u0412\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044 label.vlan.id=ID VLAN label.vlan.range=\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d VLAN label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=\u0414\u0438\u0430\u043f\u0430\u0437\u043e\u043d Range +label.vxlan=VXLAN label.vm.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0430\u0448\u0438\u043d\u044b label.vm.destroy=\u0423\u043d\u0438\u0447\u0442\u043e\u0436\u0438\u0442\u044c label.vm.display.name=\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0435 \u0438\u043c\u044f \u0412\u041c diff --git a/client/WEB-INF/classes/resources/messages_zh_CN.properties b/client/WEB-INF/classes/resources/messages_zh_CN.properties index 6ab251faaed..acb67bb51f7 100644 --- a/client/WEB-INF/classes/resources/messages_zh_CN.properties +++ b/client/WEB-INF/classes/resources/messages_zh_CN.properties @@ -314,6 +314,7 @@ label.add.template=\u6dfb\u52a0\u6a21\u677f label.add.to.group=\u6dfb\u52a0\u5230\u7ec4 label.add.user=\u6dfb\u52a0\u7528\u6237 label.add.vlan=\u6dfb\u52a0 VLAN +label.add.vxlan=\u6dfb\u52a0 VXLAN label.add.VM.to.tier=\u5411\u5c42\u4e2d\u6dfb\u52a0 VM label.add.vm=\u6dfb\u52a0 VM label.add.vms.to.lb=\u5411\u8d1f\u8f7d\u5e73\u8861\u5668\u89c4\u5219\u4e2d\u6dfb\u52a0 VM @@ -539,6 +540,7 @@ label.end.IP=\u7ed3\u675f IP label.end.port=\u7ed3\u675f\u7aef\u53e3 label.end.reserved.system.IP=\u7ed3\u675f\u9884\u7559\u7cfb\u7edf IP label.end.vlan=\u7ed3\u675f VLAN +label.end.vxlan=\u7ed3\u675f VXLAN label.endpoint.or.operation=\u7aef\u70b9\u6216\u64cd\u4f5c label.endpoint=\u7aef\u70b9 label.enter.token=\u8f93\u5165\u4ee4\u724c @@ -1025,12 +1027,14 @@ label.source.nat=\u6e90 NAT label.source=\u6e90\u7b97\u6cd5 label.specify.IP.ranges=\u6307\u5b9a IP \u8303\u56f4 label.specify.vlan=\u6307\u5b9a VLAN +label.specify.vxlan=\u6307\u5b9a VXLAN label.SR.name = SR \u540d\u79f0\u6807\u7b7e label.srx=SRX label.start.IP=\u8d77\u59cb IP label.start.port=\u8d77\u59cb\u7aef\u53e3 label.start.reserved.system.IP=\u8d77\u59cb\u9884\u7559\u7cfb\u7edf IP label.start.vlan=\u8d77\u59cb VLAN +label.start.vxlan=\u8d77\u59cb VXLAN label.state=\u72b6\u6001 label.static.nat.enabled=\u5df2\u542f\u7528\u9759\u6001 NAT label.static.nat.to=\u9759\u6001 NAT \u76ee\u6807 @@ -1157,6 +1161,9 @@ label.virtual.routers=\u865a\u62df\u8def\u7531\u5668 label.vlan.id=VLAN ID label.vlan.range=VLAN \u8303\u56f4 label.vlan=VLAN +label.vxlan.id=VXLAN ID +label.vxlan.range=VXLAN Range +label.vxlan=VXLAN label.vm.add=\u6dfb\u52a0\u5b9e\u4f8b label.vm.destroy=\u9500\u6bc1 label.vm.display.name=VM \u663e\u793a\u540d\u79f0 diff --git a/client/pom.xml b/client/pom.xml index 009bc71a691..fd1f13a173e 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -105,6 +105,11 @@ cloud-plugin-network-internallb ${project.version} + + org.apache.cloudstack + cloud-plugin-network-vxlan + ${project.version} + org.apache.cloudstack cloud-plugin-hypervisor-xen diff --git a/client/tomcatconf/componentContext.xml.in b/client/tomcatconf/componentContext.xml.in index 315c95bce27..df5b002f39b 100644 --- a/client/tomcatconf/componentContext.xml.in +++ b/client/tomcatconf/componentContext.xml.in @@ -91,6 +91,13 @@ + + + + + + + 4.0.0 + cloud-plugin-network-vxlan + Apache CloudStack Plugin - Network VXLAN + + org.apache.cloudstack + cloudstack-plugins + 4.3.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java b/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java new file mode 100644 index 00000000000..e2ba8689ad8 --- /dev/null +++ b/plugins/network-elements/vxlan/src/com/cloud/network/guru/VxlanGuestNetworkGuru.java @@ -0,0 +1,179 @@ +// 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.network.guru; + +import javax.ejb.Local; + +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; +import com.cloud.event.EventVO; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.Network; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.State; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetwork.IsolationMethod; +import com.cloud.network.dao.NetworkVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Component +@Local(value=NetworkGuru.class) +public class VxlanGuestNetworkGuru extends GuestNetworkGuru { + private static final Logger s_logger = Logger.getLogger(VxlanGuestNetworkGuru.class); + + public VxlanGuestNetworkGuru() { + super(); + _isolationMethods = new IsolationMethod[] { IsolationMethod.VXLAN }; + } + + @Override + protected boolean canHandle(NetworkOffering offering, final NetworkType networkType, final PhysicalNetwork physicalNetwork) { + // This guru handles only Guest Isolated network that supports Source nat service + if (networkType == NetworkType.Advanced + && isMyTrafficType(offering.getTrafficType()) + && offering.getGuestType() == Network.GuestType.Isolated + && isMyIsolationMethod(physicalNetwork)) { + return true; + } else { + s_logger.trace("We only take care of Guest networks of type " + GuestType.Isolated + " in zone of type " + NetworkType.Advanced); + return false; + } + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + + NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, owner); + if (network == null) { + return null; + } + + network.setBroadcastDomainType(BroadcastDomainType.Vxlan); + + return network; + } + + protected void allocateVnet(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId) throws InsufficientVirtualNetworkCapcityException { + if (network.getBroadcastUri() == null) { + String vnet = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), reservationId, + UseSystemGuestVlans.valueIn(network.getAccountId())); + if (vnet == null) { + throw new InsufficientVirtualNetworkCapcityException("Unable to allocate vnet as a " + + "part of network " + network + " implement ", DataCenter.class, dcId); + } + implemented.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(vnet)); + allocateVnetComplete(network, implemented, dcId, physicalNetworkId, reservationId, vnet); + } else { + implemented.setBroadcastUri(network.getBroadcastUri()); + } + } + + // For Test: Mockit cannot mock static method, wrap it + protected void allocateVnetComplete(Network network, NetworkVO implemented, long dcId, + long physicalNetworkId, String reservationId, String vnet) { + //TODO(VXLAN): Add new event type for vxlan? + ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), + EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone vNet: " + vnet + " Network Id: " + network.getId(), 0); + } + + @Override + public Network implement(Network network, NetworkOffering offering, + DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException { + assert (network.getState() == State.Implementing) : "Why are we implementing " + network; + + long dcId = dest.getDataCenter().getId(); + + //get physical network id + Long physicalNetworkId = network.getPhysicalNetworkId(); + + // physical network id can be null in Guest Network in Basic zone, so locate the physical network + if (physicalNetworkId == null) { + physicalNetworkId = _networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType()); + } + + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), network.getBroadcastDomainType(), network.getNetworkOfferingId(), State.Allocated, + network.getDataCenterId(), physicalNetworkId); + + allocateVnet(network, implemented, dcId, physicalNetworkId, context.getReservationId()); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + return implemented; + } + + @Override + public void reserve(NicProfile nic, Network network, + VirtualMachineProfile vm, + DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, + InsufficientAddressCapacityException { + super.reserve(nic, network, vm, dest, context); + } + + @Override + public boolean release(NicProfile nic, + VirtualMachineProfile vm, + String reservationId) { + return super.release(nic, vm, reservationId); + } + + @Override + public void shutdown(NetworkProfile profile, NetworkOffering offering) { + NetworkVO networkObject = _networkDao.findById(profile.getId()); + if (networkObject.getBroadcastDomainType() != BroadcastDomainType.Vxlan || + networkObject.getBroadcastUri() == null) { + s_logger.warn("BroadcastUri is empty or incorrect for guestnetwork " + networkObject.getDisplayText()); + return; + } + + super.shutdown(profile, offering); + } + + @Override + public boolean trash(Network network, NetworkOffering offering) { + return super.trash(network, offering); + } + + + + + +} diff --git a/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java b/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java new file mode 100644 index 00000000000..fc1767444a3 --- /dev/null +++ b/plugins/network-elements/vxlan/test/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java @@ -0,0 +1,274 @@ +// 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.network.guru; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Command; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.domain.Domain; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.Network; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Service; +import com.cloud.network.Network.State; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.Networks.TrafficType; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.server.ConfigurationServer; +import com.cloud.user.Account; +import com.cloud.vm.ReservationContext; + +import java.util.Arrays; + +public class VxlanGuestNetworkGuruTest { + PhysicalNetworkDao physnetdao = mock (PhysicalNetworkDao.class); + DataCenterDao dcdao = mock(DataCenterDao.class); + AgentManager agentmgr = mock (AgentManager.class); + NetworkOrchestrationService netmgr = mock (NetworkOrchestrationService.class); + NetworkModel netmodel = mock (NetworkModel.class); + ConfigurationServer confsvr = mock(ConfigurationServer.class); + + NetworkDao netdao = mock(NetworkDao.class); + VxlanGuestNetworkGuru guru; + + @Before + public void setUp() { + guru = spy( new VxlanGuestNetworkGuru() ); + ((GuestNetworkGuru) guru)._physicalNetworkDao = physnetdao; + guru._physicalNetworkDao = physnetdao; + guru._dcDao = dcdao; + guru._networkModel = netmodel; + guru._networkDao = netdao; + ((GuestNetworkGuru) guru)._configServer = confsvr; + + DataCenterVO dc = mock(DataCenterVO.class); + when(dc.getNetworkType()).thenReturn(NetworkType.Advanced); + when(dc.getGuestNetworkCidr()).thenReturn("10.1.1.1/24"); + + when(dcdao.findById(anyLong())).thenReturn((DataCenterVO) dc); + } + + @Test + public void testCanHandle() { + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + assertTrue(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported TrafficType != Guest + when(offering.getTrafficType()).thenReturn(TrafficType.Management); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported: GuestType Shared + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Shared); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + // Not supported: Basic networking + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + assertFalse(guru.canHandle(offering, NetworkType.Basic, physnet) == true); + + // Not supported: IsolationMethod != VXLAN + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VLAN" })); + assertFalse(guru.canHandle(offering, NetworkType.Advanced, physnet) == true); + + } + + @Test + public void testDesign() { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + DeploymentPlan plan = mock(DeploymentPlan.class); + Network network = mock(Network.class); + Account account = mock(Account.class); + + Network designednetwork = guru.design(offering, plan, network, account); + assertTrue(designednetwork != null); + assertTrue(designednetwork.getBroadcastDomainType() == BroadcastDomainType.Vxlan); + } + + @Test + public void testImplement() throws InsufficientVirtualNetworkCapcityException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getPhysicalNetworkId()).thenReturn(42L); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + //TODO(VXLAN): doesn't support VNI specified + //when(confsvr.getConfigValue((String) any(), (String) any(), anyLong())).thenReturn("true"); + when(dcdao.allocateVnet(anyLong(), anyLong(), anyLong(), (String) any(), eq(true))).thenReturn("42"); + doNothing().when(guru).allocateVnetComplete((Network) any(), (NetworkVO) any(), anyLong(), anyLong(), (String) any(), eq("42")); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + Network implementednetwork = guru.implement(network, offering, dest, res); + assertTrue(implementednetwork != null); + } + + @Test + public void testImplementWithCidr() throws InsufficientVirtualNetworkCapcityException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getGateway()).thenReturn("10.1.1.1"); + when(network.getCidr()).thenReturn("10.1.1.0/24"); + when(network.getPhysicalNetworkId()).thenReturn(42L); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + + //TODO(VXLAN): doesn't support VNI specified + //when(confsvr.getConfigValue((String) any(), (String) any(), anyLong())).thenReturn("true"); + when(dcdao.allocateVnet(anyLong(), anyLong(), anyLong(), (String) any(), eq(true))).thenReturn("42"); + doNothing().when(guru).allocateVnetComplete((Network) any(), (NetworkVO) any(), anyLong(), anyLong(), (String) any(), eq("42")); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + Network implementednetwork = guru.implement(network, offering, dest, res); + assertTrue(implementednetwork != null); + assertTrue(implementednetwork.getCidr().equals("10.1.1.0/24")); + assertTrue(implementednetwork.getGateway().equals("10.1.1.1")); + } + + @Test + public void testShutdown() throws InsufficientVirtualNetworkCapcityException, URISyntaxException { + PhysicalNetworkVO physnet = mock(PhysicalNetworkVO.class); + when(physnetdao.findById(anyLong())).thenReturn(physnet); + when(physnet.getIsolationMethods()).thenReturn(Arrays.asList(new String[] { "VXLAN" })); + when(physnet.getId()).thenReturn(42L); + + NetworkOffering offering = mock(NetworkOffering.class); + when(offering.getId()).thenReturn(42L); + when(offering.getTrafficType()).thenReturn(TrafficType.Guest); + when(offering.getGuestType()).thenReturn(GuestType.Isolated); + + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Implementing); + when(network.getBroadcastDomainType()).thenReturn(BroadcastDomainType.Vxlan); + when(network.getBroadcastUri()).thenReturn(new URI("vxlan:12345")); + when(network.getPhysicalNetworkId()).thenReturn(42L); + when(netdao.findById(42L)).thenReturn(network); + + DeployDestination dest = mock(DeployDestination.class); + + DataCenter dc = mock(DataCenter.class); + when(dest.getDataCenter()).thenReturn(dc); + + when(netmodel.findPhysicalNetworkId(anyLong(), (String) any(), (TrafficType) any())).thenReturn(42L); + + Domain dom = mock(Domain.class); + when(dom.getName()).thenReturn("domain"); + + Account acc = mock(Account.class); + when(acc.getAccountName()).thenReturn("accountname"); + + ReservationContext res = mock(ReservationContext.class); + when(res.getDomain()).thenReturn(dom); + when(res.getAccount()).thenReturn(acc); + + NetworkProfile implementednetwork = mock(NetworkProfile.class); + when(implementednetwork.getId()).thenReturn(42L); + when(implementednetwork.getBroadcastUri()).thenReturn(new URI("vxlan:12345")); + when(offering.getSpecifyVlan()).thenReturn(false); + + guru.shutdown(implementednetwork, offering); + verify(implementednetwork, times(1)).setBroadcastUri(null); + } +} diff --git a/plugins/pom.xml b/plugins/pom.xml index ff18e837714..ca41dff9376 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -62,6 +62,7 @@ alert-handlers/snmp-alerts alert-handlers/syslog-alerts network-elements/internal-loadbalancer + network-elements/vxlan diff --git a/scripts/vm/network/vnet/modifyvxlan.sh b/scripts/vm/network/vnet/modifyvxlan.sh new file mode 100755 index 00000000000..45d141a70db --- /dev/null +++ b/scripts/vm/network/vnet/modifyvxlan.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +# 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. + +# modifyvnet.sh -- adds and deletes VXLANs from a Routing Server +# set -x + +## TODO(VXLAN): MTU, IPv6 underlying + +usage() { + printf "Usage: %s: -o (add | delete) -v -p -b \n" +} + +addVxlan() { + local vxlanId=$1 + local pif=$2 + local vxlanDev=vxlan$vxlanId + local vxlanBr=$3 + local mcastGrp="239.$(( $vxlanId >> 16 % 256 )).$(( $vxlanId >> 8 % 256 )).$(( $vxlanId % 256 ))" + + ## TODO(VXLAN): $brif (trafficlabel) should be passed from caller because we cannot assume 1:1 mapping between pif and brif. + # lookup bridge interface + local sysfs_dir=/sys/devices/virtual/net/ + local brif=`find ${sysfs_dir}*/brif/ -name $pif | sed -e "s,$sysfs_dir,," | sed -e 's,/brif/.*$,,'` + + if [ "$brif " == " " ] + then + printf "Failed to lookup bridge interface which includes pif: $pif." + return 1 + fi + + # confirm ip address of $brif + ip addr show $brif | grep -w inet + if [ $? -gt 0 ] + then + printf "Failed to find vxlan multicast source ip address on brif: $brif." + return 1 + fi + + # mcast route + ## TODO(VXLAN): Can we assume there're only one IP address which can be multicast src IP on the IF? + ip route get $mcastGrp | grep -w "dev $brif" + if [ $? -gt 0 ] + then + ip route add $mcastGrp/32 dev $brif + if [ $? -gt 0 ] + then + printf "Failed to add vxlan multicast route on brif: $brif." + return 1 + fi + fi + + if [ ! -d /sys/class/net/$vxlanDev ] + then + ip link add $vxlanDev type vxlan id $vxlanId group $mcastGrp ttl 10 dev $brif + + if [ $? -gt 0 ] + then + # race condition that someone already creates the vxlan + if [ ! -d /sys/class/net/$vxlanDev ] + then + printf "Failed to create vxlan $vxlanId on brif: $brif." + return 1 + fi + fi + fi + + # is up? + ip link show $vxlanDev | grep -w UP > /dev/null + if [ $? -gt 0 ] + then + ip link set $vxlanDev up > /dev/null + fi + + if [ ! -d /sys/class/net/$vxlanBr ] + then + brctl addbr $vxlanBr > /dev/null + + if [ $? -gt 0 ] + then + if [ ! -d /sys/class/net/$vxlanBr ] + then + printf "Failed to create br: $vxlanBr" + return 2 + fi + fi + + brctl setfd $vxlanBr 0 + fi + + #pif is eslaved into vxlanBr? + ls /sys/class/net/$vxlanBr/brif/ | grep -w "$vxlanDev" > /dev/null + if [ $? -gt 0 ] + then + brctl addif $vxlanBr $vxlanDev > /dev/null + if [ $? -gt 0 ] + then + ls /sys/class/net/$vxlanBr/brif/ | grep -w "$vxlanDev" > /dev/null + if [ $? -gt 0 ] + then + printf "Failed to add vxlan: $vxlanDev to $vxlanBr" + return 3 + fi + fi + fi + + # is vxlanBr up? + ip link show $vxlanBr | grep -w UP > /dev/null + if [ $? -gt 0 ] + then + ip link set $vxlanBr up + fi + + return 0 +} + +deleteVxlan() { + local vxlanId=$1 + local pif=$2 + local vxlanDev=vxlan$vxlanId + local vxlanBr=$3 + local mcastGrp="239.$(( $vxlanId >> 16 % 256 )).$(( $vxlanId >> 8 % 256 )).$(( $vxlanId % 256 ))" + + ip route del $mcastGrp/32 dev $brif + + ip link delete $vxlanDev + + if [ $? -gt 0 ] + then + printf "Failed to del vxlan: $vxlanId" + printf "Continue..." + fi + + ip link set $vxlanBr down + + if [ $? -gt 0 ] + then + return 1 + fi + + brctl delbr $vxlanBr + + if [ $? -gt 0 ] + then + printf "Failed to del bridge $vxlanBr" + return 1 + fi + + return 0 +} + +op= +vxlanId= +option=$@ + +while getopts 'o:v:p:b:' OPTION +do + case $OPTION in + o) oflag=1 + op="$OPTARG" + ;; + v) vflag=1 + vxlanId="$OPTARG" + ;; + p) pflag=1 + pif="$OPTARG" + ;; + b) bflag=1 + brName="$OPTARG" + ;; + ?) usage + exit 2 + ;; + esac +done + +# Check that all arguments were passed in +if [ "$oflag$vflag$pflag$bflag" != "1111" ] +then + usage + exit 2 +fi + +# Do we support Vxlan? +lsmod|grep ^vxlan >& /dev/null +if [ $? -gt 0 ] +then + modprobe=`modprobe vxlan 2>&1` + if [ $? -gt 0 ] + then + printf "Failed to load vxlan kernel module: $modprobe" + exit 1 + fi +fi + +if [ "$op" == "add" ] +then + # Add the vxlan + addVxlan $vxlanId $pif $brName + + # If the add fails then return failure + if [ $? -gt 0 ] + then + exit 1 + fi +else + if [ "$op" == "delete" ] + then + # Delete the vxlan + deleteVxlan $vxlanId $pif $brName + + # Always exit with success + exit 0 + fi +fi + diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 52520d43c86..4dbf48693f0 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -190,6 +190,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { private static final long MAX_VLAN_ID = 4095L; // 2^12 - 1 private static final long MIN_GRE_KEY = 0L; private static final long MAX_GRE_KEY = 4294967295L; // 2^32 -1 + private static final long MIN_VXLAN_VNI = 0L; + private static final long MAX_VXLAN_VNI = 16777215L; // 2^24 -1 @Inject DataCenterDao _dcDao = null; @@ -2648,6 +2650,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (network.getIsolationMethods().contains("GRE")) { minVnet = MIN_GRE_KEY; maxVnet = MAX_GRE_KEY; + } else if (network.getIsolationMethods().contains("VXLAN")) { + minVnet = MIN_VXLAN_VNI; + maxVnet = MAX_VXLAN_VNI; } String rangeMessage = " between " + minVnet + " and " + maxVnet; if (VnetRange.length == 1 && VnetRange[0].equals("")) { diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index f2505d32a39..f93f9dcf85f 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -331,6 +331,7 @@ dictionary = { 'label.add.to.group': '', 'label.add.user': '', 'label.add.vlan': '', +'label.add.vxlan': '', 'label.add.vm': '', 'label.add.vms': '', 'label.add.vms.to.lb': '', @@ -548,6 +549,7 @@ dictionary = { 'label.end.port': '', 'label.end.reserved.system.IP': '', 'label.end.vlan': '', +'label.end.vxlan': '', 'label.enter.token': '', 'label.error.code': '', 'label.error': '', @@ -1014,12 +1016,14 @@ dictionary = { 'label.source.nat': '', 'label.specify.IP.ranges': '', 'label.specify.vlan': '', +'label.specify.vxlan': '', 'label.SR.name ': '', 'label.srx': '', 'label.start.IP': '', 'label.start.port': '', 'label.start.reserved.system.IP': '', 'label.start.vlan': '', +'label.start.vxlan': '', 'label.state': '', 'label.static.nat': '', 'label.static.nat.to': '', @@ -1138,6 +1142,9 @@ dictionary = { 'label.vlan': '', 'label.vlan.id': '', 'label.vlan.range': '', +'label.vxlan': '', +'label.vxlan.id': '', +'label.vxlan.range': '', 'label.vm.add': '', 'label.vm.destroy': '', 'label.vm.display.name': '', diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index f1c4ad63c1e..d2c776cb0d8 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -1168,9 +1168,12 @@ var addExtraPropertiesToGuestNetworkObject = function(jsonObj) { jsonObj.scope = "Account (" + jsonObj.domain + ", " + jsonObj.account + ")"; } - if (jsonObj.vlan == null && jsonObj.broadcasturi != null) { + if (jsonObj.vlan == null && jsonObj.broadcasturi != null && jsonObj.broadcasturi.substring(0,7) == "vlan://") { jsonObj.vlan = jsonObj.broadcasturi.replace("vlan://", ""); } + if(jsonObj.vxlan == null && jsonObj.broadcasturi != null && jsonObj.broadcasturi.substring(0,8) == "vxlan://") { + jsonObj.vxlan = jsonObj.broadcasturi.replace("vxlan://", ""); + } } //used by infrastructure page diff --git a/ui/scripts/ui-custom/zoneWizard.js b/ui/scripts/ui-custom/zoneWizard.js index cf52107ed33..28df1933fb2 100644 --- a/ui/scripts/ui-custom/zoneWizard.js +++ b/ui/scripts/ui-custom/zoneWizard.js @@ -715,10 +715,12 @@ $('