diff --git a/README.md b/README.md
index 3d9bc3d4fe36..928b8a92a6b6 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,7 @@ via Github pull requests.
* Design [documents](https://cwiki.apache.org/confluence/display/CLOUDSTACK/Design)
* API [documentation](https://cloudstack.apache.org/api.html)
* How to [contribute](CONTRIBUTING.md)
+* Check the [YouTube channel](https://www.youtube.com/ApacheCloudStack) for presentations, interviews, and more
## Getting Involved and Contributing
diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties
index 06d8f3f2a1ef..25863276c091 100644
--- a/agent/conf/agent.properties
+++ b/agent/conf/agent.properties
@@ -266,3 +266,10 @@ iscsi.session.cleanup.enabled=false
# Automatically clean up iscsi sessions not attached to any VM.
# Should be enabled for users using managed storage for example solidfire.
# Should be disabled for users with unmanaged iscsi connections on their hosts
+
+# This parameter specifies the heartbeat update timeout in ms; The default value is 60000ms (1 min).
+# Depending on the use case, this timeout might need increasing/decreasing.
+# heartbeat.update.timeout=60000
+
+# Enable manually setting CPU's topology on KVM's VM.
+# enable.manually.setting.cpu.topology.on.kvm.vm=true
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
new file mode 100644
index 000000000000..d04e98fb704d
--- /dev/null
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Licensed 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.agent.properties;
+
+/**
+ * Class of constant agent's properties available to configure on
+ * "agent.properties".
+ *
+ * Not all available agent properties are defined here, but we should work to
+ * migrate them on demand to this class.
+ *
+ * @param type of the default value.
+ */
+public class AgentProperties{
+
+ /**
+ * Heartbeat update timeout.
+ * Data type: int.
+ * Default value: 60000 (ms).
+ */
+ public static final Property HEARTBEAT_UPDATE_TIMEOUT = new Property("heartbeat.update.timeout", 60000);
+
+ /**
+ * Enable manually setting CPU's topology on KVM's VM.
+ * Data type: boolean.
+ * Default value: true.
+ */
+ public static final Property ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM = new Property("enable.manually.setting.cpu.topology.on.kvm.vm", true);
+
+ public static class Property {
+ private final String name;
+ private final T defaultValue;
+
+ private Property(String name, T value) {
+ this.name = name;
+ this.defaultValue = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public T getDefaultValue() {
+ return defaultValue;
+ }
+ }
+}
diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java b/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java
new file mode 100644
index 000000000000..6d515f0004f0
--- /dev/null
+++ b/agent/src/main/java/com/cloud/agent/properties/AgentPropertiesFileHandler.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed 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.agent.properties;
+
+import com.cloud.utils.PropertiesUtil;
+import java.io.File;
+import java.io.IOException;
+import org.apache.cloudstack.utils.security.KeyStoreUtils;
+import org.apache.commons.beanutils.ConvertUtils;
+import org.apache.commons.beanutils.converters.IntegerConverter;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+/**
+ * This class provides a facility to read the agent's properties file and get
+ * its properties, according to the {@link AgentProperties} properties constants.
+ *
+ */
+public class AgentPropertiesFileHandler {
+
+ private static final Logger logger = Logger.getLogger(AgentPropertiesFileHandler.class);
+
+ /**
+ * This method reads the property in the agent.properties file.
+ *
+ * @param property the property to retrieve.
+ * @return The value of the property. If the property is not available, the
+ * default defined value will be used.
+ */
+ public static T getPropertyValue(AgentProperties.Property property) {
+ T defaultValue = property.getDefaultValue();
+ String name = property.getName();
+
+ File agentPropertiesFile = PropertiesUtil.findConfigFile(KeyStoreUtils.AGENT_PROPSFILE);
+
+ if (agentPropertiesFile != null) {
+ try {
+ String configValue = PropertiesUtil.loadFromFile(agentPropertiesFile).getProperty(name);
+ if (StringUtils.isNotBlank(configValue)) {
+ if (defaultValue instanceof Integer) {
+ ConvertUtils.register(new IntegerConverter(defaultValue), Integer.class);
+ }
+
+ return (T)ConvertUtils.convert(configValue, defaultValue.getClass());
+ } else {
+ logger.debug(String.format("Property [%s] has empty or null value. Using default value [%s].", name, defaultValue));
+ }
+ } catch (IOException ex) {
+ logger.debug(String.format("Failed to get property [%s]. Using default value [%s].", name, defaultValue), ex);
+ }
+ } else {
+ logger.debug(String.format("File [%s] was not found, we will use default defined values. Property [%s]: [%s].", KeyStoreUtils.AGENT_PROPSFILE, name, defaultValue));
+ }
+
+ return defaultValue;
+ }
+
+}
diff --git a/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java b/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java
new file mode 100644
index 000000000000..d91d0e03fb29
--- /dev/null
+++ b/agent/src/test/java/com/cloud/agent/properties/AgentPropertiesFileHandlerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.agent.properties;
+
+import com.cloud.utils.PropertiesUtil;
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+import junit.framework.TestCase;
+import org.apache.commons.beanutils.ConvertUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({PropertiesUtil.class, ConvertUtils.class})
+public class AgentPropertiesFileHandlerTest extends TestCase {
+
+ @Mock
+ AgentProperties.Property agentPropertiesStringMock;
+
+ @Mock
+ AgentProperties.Property agentPropertiesIntegerMock;
+
+ @Mock
+ File fileMock;
+
+ @Mock
+ Properties propertiesMock;
+
+ @Test
+ public void validateGetPropertyValueFileNotFoundReturnDefaultValue() throws Exception{
+ String expectedResult = "default value";
+ Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(null).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+
+ String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+
+ @Test
+ public void validateGetPropertyValueLoadFromFileThrowsIOExceptionReturnDefaultValue() throws Exception{
+ String expectedResult = "default value";
+ Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+ PowerMockito.doThrow(new IOException()).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+
+ String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+
+ @Test
+ public void validateGetPropertyValuePropertyIsEmptyReturnDefaultValue() throws Exception{
+ String expectedResult = "default value";
+ Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
+ Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+ PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+ PowerMockito.doReturn("").when(propertiesMock).getProperty(Mockito.anyString());
+
+ String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+
+ @Test
+ public void validateGetPropertyValuePropertyIsNullReturnDefaultValue() throws Exception{
+ String expectedResult = "default value";
+ Mockito.doReturn(expectedResult).when(agentPropertiesStringMock).getDefaultValue();
+ Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+ PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+ PowerMockito.doReturn(null).when(propertiesMock).getProperty(Mockito.anyString());
+
+ String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+
+ @Test
+ public void validateGetPropertyValueValidPropertyReturnPropertyValue() throws Exception{
+ String expectedResult = "test";
+ Mockito.doReturn("default value").when(agentPropertiesStringMock).getDefaultValue();
+ Mockito.doReturn("name").when(agentPropertiesStringMock).getName();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+ PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+ Mockito.doReturn(expectedResult).when(propertiesMock).getProperty(Mockito.anyString());
+
+ String result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesStringMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+
+ @Test
+ public void validateGetPropertyValueValidIntegerPropertyReturnPropertyValue() throws Exception{
+ Integer expectedResult = 2;
+ Mockito.doReturn(1).when(agentPropertiesIntegerMock).getDefaultValue();
+ Mockito.doReturn("name").when(agentPropertiesIntegerMock).getName();
+
+ PowerMockito.mockStatic(PropertiesUtil.class);
+ PowerMockito.doReturn(fileMock).when(PropertiesUtil.class, "findConfigFile", Mockito.anyString());
+ PowerMockito.doReturn(propertiesMock).when(PropertiesUtil.class, "loadFromFile", Mockito.any());
+ Mockito.doReturn(String.valueOf(expectedResult)).when(propertiesMock).getProperty(Mockito.anyString());
+
+ Integer result = AgentPropertiesFileHandler.getPropertyValue(agentPropertiesIntegerMock);
+
+ Assert.assertEquals(expectedResult, result);
+ }
+}
diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index c4729383dd4f..8a30b5ef9fee 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -413,4 +413,9 @@ public DeployAsIsInfoTO getDeployAsIsInfo() {
public void setDeployAsIsInfo(DeployAsIsInfoTO deployAsIsInfo) {
this.deployAsIsInfo = deployAsIsInfo;
}
+
+ @Override
+ public String toString() {
+ return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type);
+ }
}
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index fe603d1f411c..85e9aa58a71c 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -352,6 +352,10 @@ public class EventTypes {
// Host
public static final String EVENT_HOST_RECONNECT = "HOST.RECONNECT";
+ // Host on Degraded ResourceState
+ public static final String EVENT_DECLARE_HOST_DEGRADED = "HOST.DECLARE.DEGRADED";
+ public static final String EVENT_CANCEL_HOST_DEGRADED = "HOST.CANCEL.DEGRADED";
+
// Host Out-of-band management
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_ENABLE = "HOST.OOBM.ENABLE";
public static final String EVENT_HOST_OUTOFBAND_MANAGEMENT_DISABLE = "HOST.OOBM.DISABLE";
diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java
index 37b8f332d0a9..111b85a6d96d 100644
--- a/api/src/main/java/com/cloud/network/Network.java
+++ b/api/src/main/java/com/cloud/network/Network.java
@@ -19,6 +19,7 @@
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import com.cloud.exception.InvalidParameterValueException;
@@ -456,4 +457,6 @@ public void setIp6Address(String ip6Address) {
String getRouterIp();
String getRouterIpv6();
+
+ Date getCreated();
}
diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java
index 08a420a1b20f..f3c178767e85 100644
--- a/api/src/main/java/com/cloud/network/NetworkProfile.java
+++ b/api/src/main/java/com/cloud/network/NetworkProfile.java
@@ -17,6 +17,7 @@
package com.cloud.network;
import java.net.URI;
+import java.util.Date;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.Mode;
@@ -329,4 +330,9 @@ public String getRouterIpv6() {
return null;
}
+ @Override
+ public Date getCreated() {
+ return null;
+ }
+
}
diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java b/api/src/main/java/com/cloud/network/vpc/Vpc.java
index 9f40562423d8..432c8839ad89 100644
--- a/api/src/main/java/com/cloud/network/vpc/Vpc.java
+++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java
@@ -20,6 +20,8 @@
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
+import java.util.Date;
+
public interface Vpc extends ControlledEntity, Identity, InternalIdentity {
public enum State {
@@ -91,4 +93,6 @@ public enum State {
boolean isRollingRestart();
void setRollingRestart(boolean rollingRestart);
+
+ Date getCreated();
}
diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java
index 8ae90c574224..f01c58542e3e 100644
--- a/api/src/main/java/com/cloud/offering/NetworkOffering.java
+++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java
@@ -23,6 +23,8 @@
import com.cloud.network.Network.GuestType;
import com.cloud.network.Networks.TrafficType;
+import java.util.Date;
+
/**
* Describes network offering
*
@@ -141,4 +143,6 @@ public enum Detail {
boolean isSupportingPublicAccess();
String getServicePackage();
+
+ Date getCreated();
}
diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java b/api/src/main/java/com/cloud/resource/ResourceService.java
index 7f04d8919b97..e2b84ba87203 100644
--- a/api/src/main/java/com/cloud/resource/ResourceService.java
+++ b/api/src/main/java/com/cloud/resource/ResourceService.java
@@ -24,10 +24,12 @@
import org.apache.cloudstack.api.command.admin.host.AddHostCmd;
import org.apache.cloudstack.api.command.admin.host.AddSecondaryStorageCmd;
import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd;
-import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd;
import org.apache.cloudstack.api.command.admin.host.ReconnectHostCmd;
import org.apache.cloudstack.api.command.admin.host.UpdateHostCmd;
import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd;
+import org.apache.cloudstack.api.command.admin.host.PrepareForMaintenanceCmd;
+import org.apache.cloudstack.api.command.admin.host.DeclareHostAsDegradedCmd;
+import org.apache.cloudstack.api.command.admin.host.CancelHostAsDegradedCmd;
import com.cloud.dc.DataCenter;
import com.cloud.exception.AgentUnavailableException;
@@ -67,6 +69,10 @@ public interface ResourceService {
Host maintain(PrepareForMaintenanceCmd cmd);
+ Host declareHostAsDegraded(DeclareHostAsDegradedCmd cmd) throws NoTransitionException;
+
+ Host cancelHostAsDegraded(CancelHostAsDegradedCmd cmd) throws NoTransitionException;
+
/**
* Deletes a host
* @param true if deleted, false otherwise
diff --git a/api/src/main/java/com/cloud/resource/ResourceState.java b/api/src/main/java/com/cloud/resource/ResourceState.java
index 6e0fa9092307..70738c7921bc 100644
--- a/api/src/main/java/com/cloud/resource/ResourceState.java
+++ b/api/src/main/java/com/cloud/resource/ResourceState.java
@@ -30,7 +30,8 @@ public enum ResourceState {
PrepareForMaintenance,
ErrorInMaintenance,
Maintenance,
- Error;
+ Error,
+ Degraded;
public enum Event {
InternalCreated("Resource is created"),
@@ -45,6 +46,8 @@ public enum Event {
ErrorsCorrected("Errors were corrected on a resource attempting to enter maintenance but encountered errors"),
Error("An internal error happened"),
DeleteHost("Admin delete a host"),
+ DeclareHostDegraded("Admin declares host as Degraded"),
+ EnableDegradedHost("Admin puts Degraded host into Enabled"),
/*
* Below events don't cause resource state to change, they are merely
@@ -113,11 +116,13 @@ public static boolean canAttemptMaintenance(ResourceState state) {
s_fsm.addTransition(ResourceState.Enabled, Event.InternalCreated, ResourceState.Enabled);
s_fsm.addTransition(ResourceState.Enabled, Event.Disable, ResourceState.Disabled);
s_fsm.addTransition(ResourceState.Enabled, Event.AdminAskMaintenance, ResourceState.PrepareForMaintenance);
+ s_fsm.addTransition(ResourceState.Enabled, Event.DeclareHostDegraded, ResourceState.Degraded);
s_fsm.addTransition(ResourceState.Enabled, Event.InternalEnterMaintenance, ResourceState.Maintenance);
s_fsm.addTransition(ResourceState.Enabled, Event.DeleteHost, ResourceState.Disabled);
s_fsm.addTransition(ResourceState.Disabled, Event.Enable, ResourceState.Enabled);
s_fsm.addTransition(ResourceState.Disabled, Event.Disable, ResourceState.Disabled);
s_fsm.addTransition(ResourceState.Disabled, Event.InternalCreated, ResourceState.Disabled);
+ s_fsm.addTransition(ResourceState.Disabled, Event.DeclareHostDegraded, ResourceState.Degraded);
s_fsm.addTransition(ResourceState.PrepareForMaintenance, Event.InternalEnterMaintenance, ResourceState.Maintenance);
s_fsm.addTransition(ResourceState.PrepareForMaintenance, Event.AdminCancelMaintenance, ResourceState.Enabled);
s_fsm.addTransition(ResourceState.PrepareForMaintenance, Event.UnableToMigrate, ResourceState.ErrorInPrepareForMaintenance);
@@ -126,6 +131,7 @@ public static boolean canAttemptMaintenance(ResourceState state) {
s_fsm.addTransition(ResourceState.Maintenance, Event.AdminCancelMaintenance, ResourceState.Enabled);
s_fsm.addTransition(ResourceState.Maintenance, Event.InternalCreated, ResourceState.Maintenance);
s_fsm.addTransition(ResourceState.Maintenance, Event.DeleteHost, ResourceState.Disabled);
+ s_fsm.addTransition(ResourceState.Maintenance, Event.DeclareHostDegraded, ResourceState.Degraded);
s_fsm.addTransition(ResourceState.ErrorInPrepareForMaintenance, Event.InternalCreated, ResourceState.ErrorInPrepareForMaintenance);
s_fsm.addTransition(ResourceState.ErrorInPrepareForMaintenance, Event.Disable, ResourceState.Disabled);
s_fsm.addTransition(ResourceState.ErrorInPrepareForMaintenance, Event.DeleteHost, ResourceState.Disabled);
@@ -141,6 +147,8 @@ public static boolean canAttemptMaintenance(ResourceState state) {
s_fsm.addTransition(ResourceState.ErrorInMaintenance, Event.AdminCancelMaintenance, ResourceState.Enabled);
s_fsm.addTransition(ResourceState.Error, Event.InternalCreated, ResourceState.Error);
s_fsm.addTransition(ResourceState.Disabled, Event.DeleteHost, ResourceState.Disabled);
-
+ s_fsm.addTransition(ResourceState.Degraded, Event.DeleteHost, ResourceState.Disabled);
+ s_fsm.addTransition(ResourceState.Degraded, Event.EnableDegradedHost, ResourceState.Enabled);
+ s_fsm.addTransition(ResourceState.Degraded, Event.AdminAskMaintenance, ResourceState.Maintenance);
}
}
diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java
index 2f3a59541d9a..9dc7d45b036e 100644
--- a/api/src/main/java/com/cloud/storage/Snapshot.java
+++ b/api/src/main/java/com/cloud/storage/Snapshot.java
@@ -26,7 +26,8 @@
public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, StateObject {
public enum Type {
- MANUAL, RECURRING, TEMPLATE, HOURLY, DAILY, WEEKLY, MONTHLY;
+ MANUAL, RECURRING, TEMPLATE, HOURLY, DAILY, WEEKLY, MONTHLY, INTERNAL;
+ // New types should be defined after INTERNAL, and change the max value
private int max = 8;
public void setMax(int max) {
@@ -71,6 +72,7 @@ enum LocationType {
}
public static final long MANUAL_POLICY_ID = 0L;
+ public static final long INTERNAL_POLICY_ID = 7L;
@Override
long getAccountId();
diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java
index 362cc2cac296..407935919ca2 100644
--- a/api/src/main/java/com/cloud/storage/Storage.java
+++ b/api/src/main/java/com/cloud/storage/Storage.java
@@ -76,6 +76,16 @@ public String getFileExtension() {
}
+ public static enum Capability {
+ HARDWARE_ACCELERATION("HARDWARE_ACCELERATION");
+
+ private final String capability;
+
+ private Capability(String capability) {
+ this.capability = capability;
+ }
+ }
+
public static enum ProvisioningType {
THIN("thin"),
SPARSE("sparse"),
diff --git a/api/src/main/java/com/cloud/storage/StorageService.java b/api/src/main/java/com/cloud/storage/StorageService.java
index 4b18739b55dc..bb086ad05cbf 100644
--- a/api/src/main/java/com/cloud/storage/StorageService.java
+++ b/api/src/main/java/com/cloud/storage/StorageService.java
@@ -26,8 +26,8 @@
import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd;
import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd;
import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
-import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
import org.apache.cloudstack.api.command.admin.storage.SyncStoragePoolCmd;
+import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
import com.cloud.exception.DiscoveryException;
import com.cloud.exception.InsufficientCapacityException;
@@ -105,6 +105,8 @@ public interface StorageService {
ImageStore updateImageStoreStatus(Long id, Boolean readonly);
+ void updateStorageCapabilities(Long poolId, boolean failOnChecks);
+
StoragePool syncStoragePool(SyncStoragePoolCmd cmd);
}
diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java
index c9a5139043f6..5eb93ca9556c 100644
--- a/api/src/main/java/com/cloud/storage/VolumeApiService.java
+++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java
@@ -22,7 +22,6 @@
import java.util.Map;
import com.cloud.exception.StorageUnavailableException;
-import org.apache.cloudstack.api.command.user.vm.CloneVMCmd;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
@@ -94,7 +93,7 @@ public interface VolumeApiService {
Volume detachVolumeViaDestroyVM(long vmId, long volumeId);
- Volume cloneDataVolume(CloneVMCmd cmd, long snapshotId, Volume volume) throws StorageUnavailableException;
+ Volume cloneDataVolume(long vmId, long snapshotId, Volume volume) throws StorageUnavailableException;
Volume detachVolumeFromVM(DetachVolumeCmd cmd);
@@ -105,7 +104,7 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc
Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long owner, String chainInfo);
- Volume attachVolumeToVm(CloneVMCmd cmd, Long volumeId, Long deviceId);
+ Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId);
/**
* Extracts the volume to a particular location.
diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java
index fd45499ef6b4..60b30745a8b0 100644
--- a/api/src/main/java/com/cloud/template/TemplateApiService.java
+++ b/api/src/main/java/com/cloud/template/TemplateApiService.java
@@ -20,7 +20,9 @@
import java.net.URISyntaxException;
import java.util.List;
+import com.cloud.storage.Snapshot;
import com.cloud.storage.VolumeApiService;
+import com.cloud.uservm.UserVm;
import org.apache.cloudstack.api.BaseListTemplateOrIsoPermissionsCmd;
import org.apache.cloudstack.api.BaseUpdateTemplateOrIsoPermissionsCmd;
import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
@@ -101,12 +103,14 @@ public interface TemplateApiService {
boolean updateTemplateOrIsoPermissions(BaseUpdateTemplateOrIsoPermissionsCmd cmd);
+ Snapshot createSnapshotFromTemplateOwner(long vmId, UserVm curVm, Account templateOwner, VolumeApiService volumeService) throws ResourceAllocationException;
+
/**
* create a template record for later usage of creating a real template by createPrivateTemplate
* */
- VirtualMachineTemplate createPrivateTemplateRecord(CloneVMCmd cmd, Account templateOwner, VolumeApiService serviceObj) throws ResourceAllocationException;
+ VirtualMachineTemplate createPrivateTemplateRecord(CloneVMCmd cmd, Account templateOwner, VolumeApiService serviceObj, Snapshot snapshot) throws ResourceAllocationException;
- VirtualMachineTemplate createPrivateTemplate(CloneVMCmd cmd) throws CloudRuntimeException;
+ VirtualMachineTemplate createPrivateTemplate(CloneVMCmd cmd, long snapshotId, long templateId) throws CloudRuntimeException;
VirtualMachineTemplate createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException;
diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java
index 4e3733bb5a49..98b1618a8da8 100644
--- a/api/src/main/java/com/cloud/user/AccountService.java
+++ b/api/src/main/java/com/cloud/user/AccountService.java
@@ -121,4 +121,6 @@ UserAccount createUserAccount(String userName, String password, String firstName
UserAccount getUserAccountById(Long userId);
public Map getKeys(GetUserKeysCmd cmd);
+
+ public Map getKeys(Long userId);
}
diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java
index da8c2373ca79..7bdbdfc802ef 100644
--- a/api/src/main/java/com/cloud/vm/UserVmService.java
+++ b/api/src/main/java/com/cloud/vm/UserVmService.java
@@ -98,8 +98,9 @@ public interface UserVmService {
* */
Optional cloneVirtualMachine(CloneVMCmd cmd, VolumeApiService volumeService, SnapshotApiService snapshotService) throws ResourceUnavailableException, ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException;
- void checkCloneCondition(CloneVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ResourceAllocationException;
+ void validateCloneCondition(CloneVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ResourceAllocationException;
+ void prepareCloneVirtualMachine(CloneVMCmd cmd) throws ResourceAllocationException, InsufficientCapacityException, ResourceUnavailableException;
/**
* Resets the password of a virtual machine.
*
@@ -444,7 +445,7 @@ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffe
UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
StorageUnavailableException, ResourceAllocationException;
- UserVm recordVirtualMachineToDB(CloneVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
+ UserVm recordVirtualMachineToDB(CloneVMCmd cmd, long templateId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
StorageUnavailableException, ResourceAllocationException;
UserVm getUserVm(long vmId);
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index b060b5a21762..48386a45bf35 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -29,6 +29,7 @@ public class ApiConstants {
public static final String ANNOTATION = "annotation";
public static final String API_KEY = "apikey";
public static final String ASYNC_BACKUP = "asyncbackup";
+ public static final String AUTO_SELECT = "autoselect";
public static final String USER_API_KEY = "userapikey";
public static final String APPLIED = "applied";
public static final String LIST_LB_VMIPS = "lbvmips";
@@ -156,6 +157,7 @@ public class ApiConstants {
public static final String FIRSTNAME = "firstname";
public static final String FORCED = "forced";
public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage";
+ public static final String FORCE_DELETE_HOST = "forcedeletehost";
public static final String FORMAT = "format";
public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork";
public static final String FOR_SYSTEM_VMS = "forsystemvms";
@@ -357,6 +359,7 @@ public class ApiConstants {
public static final String SWAP_OWNER = "swapowner";
public static final String SYSTEM_VM_TYPE = "systemvmtype";
public static final String TAGS = "tags";
+ public static final String STORAGE_TAGS = "storagetags";
public static final String TARGET_IQN = "targetiqn";
public static final String TEMPLATE_FILTER = "templatefilter";
public static final String TEMPLATE_ID = "templateid";
diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseListCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseListCmd.java
index 36fa36fcfc9a..bcebbb860033 100644
--- a/api/src/main/java/org/apache/cloudstack/api/BaseListCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/BaseListCmd.java
@@ -94,7 +94,7 @@ public Long getPageSizeVal() {
if (pageSizeInt != null) {
defaultPageSize = pageSizeInt.longValue();
}
- if (defaultPageSize.longValue() == s_pageSizeUnlimited) {
+ if (s_pageSizeUnlimited.equals(defaultPageSize)) {
defaultPageSize = null;
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java
new file mode 100644
index 000000000000..98557dd710ab
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/CancelHostAsDegradedCmd.java
@@ -0,0 +1,113 @@
+// 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 org.apache.cloudstack.api.command.admin.host;
+
+import com.cloud.event.EventTypes;
+import com.cloud.host.Host;
+import com.cloud.utils.fsm.NoTransitionException;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandJobType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.context.CallContext;
+
+@APICommand(name = "cancelHostAsDegraded",
+ description = "Cancel host status from 'Degraded'. Host will transit back to status 'Enabled'.",
+ since = "4.16.0.0",
+ responseObject = HostResponse.class,
+ requestHasSensitiveInfo = false,
+ responseHasSensitiveInfo = false,
+ authorized = {RoleType.Admin})
+public class CancelHostAsDegradedCmd extends BaseAsyncCmd {
+
+ private static final String COMMAND_RESPONSE_NAME = "cancelhostasdegradedresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "host ID", required = true, validations = {ApiArgValidator.PositiveNumber})
+ private Long id;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getId() {
+ return id;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getCommandName() {
+ return COMMAND_RESPONSE_NAME;
+ }
+
+ public static String getResultObjectName() {
+ return "host";
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return CallContext.current().getCallingAccountId();
+ }
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_CANCEL_HOST_DEGRADED;
+ }
+
+ @Override
+ public String getEventDescription() {
+ return "declaring host: " + getId() + " as Degraded";
+ }
+
+ @Override
+ public ApiCommandJobType getInstanceType() {
+ return ApiCommandJobType.Host;
+ }
+
+ @Override
+ public Long getInstanceId() {
+ return getId();
+ }
+
+ @Override
+ public void execute() {
+ Host host;
+ try {
+ host = _resourceService.cancelHostAsDegraded(this);
+ } catch (NoTransitionException exception) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to Cancel host from Degraded status due to: " + exception.getMessage());
+ }
+
+ HostResponse response = _responseGenerator.createHostResponse(host);
+ response.setResponseName(COMMAND_RESPONSE_NAME);
+ this.setResponseObject(response);
+ }
+
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java
new file mode 100644
index 000000000000..bdf440fc054f
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/DeclareHostAsDegradedCmd.java
@@ -0,0 +1,113 @@
+// 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 org.apache.cloudstack.api.command.admin.host;
+
+import com.cloud.event.EventTypes;
+import com.cloud.host.Host;
+import com.cloud.utils.fsm.NoTransitionException;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.ApiArgValidator;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandJobType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.context.CallContext;
+
+@APICommand(name = "declareHostAsDegraded",
+ description = "Declare host as 'Degraded'. Host must be on 'Disconnected' or 'Alert' state. The ADMIN must be sure that there are no VMs running on the respective host otherwise this command might corrupted VMs that were running on the 'Degraded' host.",
+ since = "4.16.0.0",
+ responseObject = HostResponse.class,
+ requestHasSensitiveInfo = false,
+ responseHasSensitiveInfo = false,
+ authorized = {RoleType.Admin})
+public class DeclareHostAsDegradedCmd extends BaseAsyncCmd {
+
+ private static final String COMMAND_RESPONSE_NAME = "declarehostasdegradedresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID, entityType = HostResponse.class, description = "host ID", required = true, validations = {ApiArgValidator.PositiveNumber})
+ private Long id;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getId() {
+ return id;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getCommandName() {
+ return COMMAND_RESPONSE_NAME;
+ }
+
+ public static String getResultObjectName() {
+ return "host";
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return CallContext.current().getCallingAccountId();
+ }
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_DECLARE_HOST_DEGRADED;
+ }
+
+ @Override
+ public String getEventDescription() {
+ return "declaring host: " + getId() + " as Degraded";
+ }
+
+ @Override
+ public ApiCommandJobType getInstanceType() {
+ return ApiCommandJobType.Host;
+ }
+
+ @Override
+ public Long getInstanceId() {
+ return getId();
+ }
+
+ @Override
+ public void execute() {
+ Host host;
+ try {
+ host = _resourceService.declareHostAsDegraded(this);
+ } catch (NoTransitionException exception) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to declare host as Degraded due to: " + exception.getMessage());
+ }
+
+ HostResponse response = _responseGenerator.createHostResponse(host);
+ response.setResponseName(COMMAND_RESPONSE_NAME);
+ this.setResponseObject(response);
+ }
+
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java
index 43a0666e934a..4212a0059e20 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java
@@ -19,12 +19,14 @@
import java.util.ArrayList;
import java.util.List;
+import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
import org.apache.log4j.Logger;
@@ -71,6 +73,20 @@ public class UpdateServiceOfferingCmd extends BaseCmd {
since = "4.13")
private String zoneIds;
+ @Parameter(name = ApiConstants.STORAGE_TAGS,
+ type = CommandType.STRING,
+ description = "comma-separated list of tags for the service offering, tags should match with existing storage pool tags",
+ authorized = {RoleType.Admin},
+ since = "4.16")
+ private String storageTags;
+
+ @Parameter(name = ApiConstants.HOST_TAGS,
+ type = CommandType.STRING,
+ description = "the host tag for this service offering.",
+ authorized = {RoleType.Admin},
+ since = "4.16")
+ private String hostTags;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -151,6 +167,14 @@ public List getZoneIds() {
return validZoneIds;
}
+ public String getStorageTags() {
+ return storageTags;
+ }
+
+ public String getHostTags() {
+ return hostTags;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
index ed123dbb6c37..2450ac7cd6ba 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java
@@ -99,7 +99,10 @@ public Long getId() {
return id;
}
- /////////////////////////////////////////////////////
+ public void setId(Long id) {
+ this.id = id;
+ }
+/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStorageCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStorageCapabilitiesCmd.java
new file mode 100644
index 000000000000..b6fb03dd7985
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateStorageCapabilitiesCmd.java
@@ -0,0 +1,86 @@
+// 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 org.apache.cloudstack.api.command.admin.storage;
+
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.StoragePoolResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+import java.util.Locale;
+
+@APICommand(name = UpdateStorageCapabilitiesCmd.APINAME, description = "Syncs capabilities of storage pools",
+ responseObject = StoragePoolResponse.class,
+ requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0")
+public class UpdateStorageCapabilitiesCmd extends BaseCmd {
+ public static final String APINAME = "updateStorageCapabilities";
+ private static final Logger LOG = Logger.getLogger(UpdateStorageCapabilitiesCmd.class.getName());
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, required = true, description = "Storage pool id")
+ private Long poolId;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getPoolId() {
+ return poolId;
+ }
+
+ public void setPoolId(Long poolId) {
+ this.poolId = poolId;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+
+ @Override
+ public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException {
+ _storageService.updateStorageCapabilities(poolId, true);
+ ListStoragePoolsCmd listStoragePoolCmd = new ListStoragePoolsCmd();
+ listStoragePoolCmd.setId(poolId);
+ ListResponse listResponse = _queryService.searchForStoragePools(listStoragePoolCmd);
+ listResponse.setResponseName(getCommandName());
+ this.setResponseObject(listResponse);
+ }
+
+ @Override
+ public String getCommandName() {
+ return APINAME.toLowerCase(Locale.ROOT) + "response" ;
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return CallContext.current().getCallingAccountId();
+ }
+}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java
index 50129a580b31..decc722e86f1 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/MigrateSystemVMCmd.java
@@ -30,6 +30,7 @@
import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.SystemVmResponse;
import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang.BooleanUtils;
import org.apache.log4j.Logger;
import com.cloud.event.EventTypes;
@@ -75,6 +76,12 @@ public class MigrateSystemVMCmd extends BaseAsyncCmd {
description = "Destination storage pool ID to migrate VM volumes to. Required for migrating the root disk volume")
private Long storageId;
+ @Parameter(name = ApiConstants.AUTO_SELECT,
+ since = "4.16.0",
+ type = CommandType.BOOLEAN,
+ description = "Automatically select a destination host which do not require storage migration, if hostId and storageId are not specified. false by default")
+ private Boolean autoSelect;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -91,6 +98,10 @@ public Long getStorageId() {
return storageId;
}
+ public Boolean isAutoSelect() {
+ return BooleanUtils.isNotFalse(autoSelect);
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@@ -122,27 +133,14 @@ public String getEventDescription() {
@Override
public void execute() {
- if (getHostId() == null && getStorageId() == null) {
- throw new InvalidParameterValueException("Either hostId or storageId must be specified");
- }
-
if (getHostId() != null && getStorageId() != null) {
throw new InvalidParameterValueException("Only one of hostId and storageId can be specified");
}
+
try {
//FIXME : Should not be calling UserVmService to migrate all types of VMs - need a generic VM layer
VirtualMachine migratedVm = null;
- if (getHostId() != null) {
- Host destinationHost = _resourceService.getHost(getHostId());
- if (destinationHost == null) {
- throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
- }
- if (destinationHost.getType() != Host.Type.Routing) {
- throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one");
- }
- CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId());
- migratedVm = _userVmService.migrateVirtualMachineWithVolume(getVirtualMachineId(), destinationHost, new HashMap());
- } else if (getStorageId() != null) {
+ if (getStorageId() != null) {
// OfflineMigration performed when this parameter is specified
StoragePool destStoragePool = _storageService.getStoragePool(getStorageId());
if (destStoragePool == null) {
@@ -150,6 +148,25 @@ public void execute() {
}
CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to storage pool Id: " + getStorageId());
migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), destStoragePool);
+ } else {
+ Host destinationHost = null;
+ if (getHostId() != null) {
+ destinationHost =_resourceService.getHost(getHostId());
+ if (destinationHost == null) {
+ throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
+ }
+ if (destinationHost.getType() != Host.Type.Routing) {
+ throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one");
+ }
+ } else if (! isAutoSelect()) {
+ throw new InvalidParameterValueException("Please specify a host or storage as destination, or pass 'autoselect=true' to automatically select a destination host which do not require storage migration");
+ }
+ CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId());
+ if (destinationHost == null) {
+ migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), null);
+ } else {
+ migratedVm = _userVmService.migrateVirtualMachineWithVolume(getVirtualMachineId(), destinationHost, new HashMap());
+ }
}
if (migratedVm != null) {
// return the generic system VM instance response
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java
index 9f73ae586a08..2c68d86f4450 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java
@@ -29,6 +29,7 @@
import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext;
+import org.apache.commons.lang.BooleanUtils;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
@@ -60,7 +61,7 @@ public class MigrateVMCmd extends BaseAsyncCmd {
type = CommandType.UUID,
entityType = HostResponse.class,
required = false,
- description = "Destination Host ID to migrate VM to. Required for live migrating a VM from host to host")
+ description = "Destination Host ID to migrate VM to.")
private Long hostId;
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID,
@@ -77,6 +78,12 @@ public class MigrateVMCmd extends BaseAsyncCmd {
description = "Destination storage pool ID to migrate VM volumes to. Required for migrating the root disk volume")
private Long storageId;
+ @Parameter(name = ApiConstants.AUTO_SELECT,
+ since = "4.16.0",
+ type = CommandType.BOOLEAN,
+ description = "Automatically select a destination host which do not require storage migration, if hostId and storageId are not specified. false by default")
+ private Boolean autoSelect;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -93,6 +100,10 @@ public Long getStoragePoolId() {
return storageId;
}
+ public Boolean isAutoSelect() {
+ return BooleanUtils.isNotFalse(autoSelect);
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@@ -132,10 +143,6 @@ public String getEventDescription() {
@Override
public void execute() {
- if (getHostId() == null && getStoragePoolId() == null) {
- throw new InvalidParameterValueException("Either hostId or storageId must be specified");
- }
-
if (getHostId() != null && getStoragePoolId() != null) {
throw new InvalidParameterValueException("Only one of hostId and storageId can be specified");
}
@@ -146,17 +153,6 @@ public void execute() {
}
Host destinationHost = null;
- if (getHostId() != null) {
- destinationHost = _resourceService.getHost(getHostId());
- if (destinationHost == null) {
- throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
- }
- if (destinationHost.getType() != Host.Type.Routing) {
- throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one");
- }
- CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId());
- }
-
// OfflineMigration performed when this parameter is specified
StoragePool destStoragePool = null;
if (getStoragePoolId() != null) {
@@ -165,13 +161,24 @@ public void execute() {
throw new InvalidParameterValueException("Unable to find the storage pool to migrate the VM");
}
CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to storage pool Id: " + getStoragePoolId());
+ } else if (getHostId() != null) {
+ destinationHost = _resourceService.getHost(getHostId());
+ if (destinationHost == null) {
+ throw new InvalidParameterValueException("Unable to find the host to migrate the VM, host id=" + getHostId());
+ }
+ if (destinationHost.getType() != Host.Type.Routing) {
+ throw new InvalidParameterValueException("The specified host(" + destinationHost.getName() + ") is not suitable to migrate the VM, please specify another one");
+ }
+ CallContext.current().setEventDetails("VM Id: " + getVirtualMachineId() + " to host Id: " + getHostId());
+ } else if (! isAutoSelect()) {
+ throw new InvalidParameterValueException("Please specify a host or storage as destination, or pass 'autoselect=true' to automatically select a destination host which do not require storage migration");
}
try {
VirtualMachine migratedVm = null;
- if (getHostId() != null) {
+ if (getStoragePoolId() == null) {
migratedVm = _userVmService.migrateVirtualMachine(getVirtualMachineId(), destinationHost);
- } else if (getStoragePoolId() != null) {
+ } else {
migratedVm = _userVmService.vmStorageMigration(getVirtualMachineId(), destStoragePool);
}
if (migratedVm != null) {
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java
index e0310a184122..ea5657cf9657 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/firewall/CreateFirewallRuleCmd.java
@@ -73,7 +73,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal
@Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of firewall rule")
private Integer publicEndPort;
- @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.")
+ @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).")
private List cidrlist;
@Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the ICMP message being sent")
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java
index 283da3cd8e41..6c1e133d57c6 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java
@@ -107,7 +107,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "the domain ID associated with the load balancer")
private Long domainId;
- @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).")
+ @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.")
private List cidrlist;
@Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "The guest network this "
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java
index 7b66bd9b5051..1e65a413fd1c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/nat/CreateIpForwardingRuleCmd.java
@@ -74,7 +74,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta
description = "if true, firewall rule for source/end public port is automatically created; if false - firewall rule has to be created explicitly. Has value true by default")
private Boolean openFirewall;
- @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,).")
+ @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the CIDR list to forward traffic from. Multiple entries must be separated by a single comma character (,). This parameter is deprecated. Do not use.")
private List cidrlist;
/////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CloneVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CloneVMCmd.java
index 136f5b9ce087..33314ac7ac89 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CloneVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CloneVMCmd.java
@@ -1,3 +1,19 @@
+// 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 org.apache.cloudstack.api.command.user.vm;
import com.cloud.event.EventTypes;
@@ -9,6 +25,7 @@
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
+import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
@@ -16,24 +33,24 @@
import org.apache.cloudstack.api.ApiCommandJobType;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
-import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd;
+import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
-//import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import java.util.Optional;
-@APICommand(name = "cloneVirtualMachine", responseObject = UserVmResponse.class, description = "clone a virtual VM in full clone mode",
- responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = {VirtualMachine.class})
-public class CloneVMCmd extends BaseAsyncCreateCustomIdCmd implements UserCmd {
+@APICommand(name = "cloneVirtualMachine", responseObject = UserVmResponse.class, description = "clone a virtual VM",
+ responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, entityType = {VirtualMachine.class}, since="4.16.0")
+public class CloneVMCmd extends BaseAsyncCreateCmd implements UserCmd {
public static final Logger s_logger = Logger.getLogger(CloneVMCmd.class.getName());
private static final String s_name = "clonevirtualmachineresponse";
+ private static final String CLONE_IDENTIFIER = "Clone";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
@@ -41,7 +58,10 @@ public class CloneVMCmd extends BaseAsyncCreateCustomIdCmd implements UserCmd {
@ACL(accessType = AccessType.OperateEntry)
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType=UserVmResponse.class,
required = true, description = "The ID of the virtual machine")
- private Long id;
+ private Long virtualmachineid;
+
+ @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the cloned virtual machine")
+ private String name;
//Owner information
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.")
@@ -50,10 +70,6 @@ public class CloneVMCmd extends BaseAsyncCreateCustomIdCmd implements UserCmd {
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.")
private Long domainId;
- private Long temporaryTemlateId;
-
- private Long temporarySnapShotId;
-
public String getAccountName() {
return accountName;
}
@@ -62,8 +78,16 @@ public Long getDomainId() {
return domainId;
}
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
public Long getId() {
- return this.id;
+ return this.virtualmachineid;
}
@Override
public String getEventType() {
@@ -80,52 +104,22 @@ public String getEventDescription() {
return "Cloning user VM: " + this._uuidMgr.getUuid(VirtualMachine.class, getId());
}
- public Long getTemporaryTemlateId() {
- return this.temporaryTemlateId;
- }
-
- public void setTemporarySnapShotId(Long snapshotId) {
- this.temporarySnapShotId = snapshotId;
- }
-
- public Long getTemporarySnapShotId() {
- return temporarySnapShotId;
- }
-
-
- public void setTemporaryTemlateId(long tempId) {
- this.temporaryTemlateId = tempId;
- }
-
@Override
public void create() throws ResourceAllocationException {
try {
- _userVmService.checkCloneCondition(this);
- VirtualMachineTemplate template = _templateService.createPrivateTemplateRecord(this, _accountService.getAccount(getEntityOwnerId()), _volumeService);
- if (template == null) {
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "failed to create a template to db");
- }
- s_logger.info("The template id recorded is: " + template.getId());
- setTemporaryTemlateId(template.getId());
- _templateService.createPrivateTemplate(this);
- _snapshotService.deleteSnapshot(getTemporarySnapShotId());
- UserVm vmRecord = _userVmService.recordVirtualMachineToDB(this);
- if (vmRecord == null) {
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "unable to record a new VM to db!");
- }
- setEntityId(vmRecord.getId());
- setEntityUuid(vmRecord.getUuid());
- } catch (ResourceUnavailableException | InsufficientCapacityException e) {
+ _userVmService.validateCloneCondition(this);
+ _userVmService.prepareCloneVirtualMachine(this);
+ }
+ catch (ResourceUnavailableException | InsufficientCapacityException e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, e.getMessage());
} catch (InvalidParameterValueException e) {
s_logger.warn("Exception: ", e);
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
- } finally {
- if (getTemporaryTemlateId() != null) {
- // TODO: delete template in the service
- s_logger.warn("clearing the temporary template: " + getTemporaryTemlateId());
- }
+ } catch (ServerApiException e) {
+ throw new ServerApiException(e.getErrorCode(), e.getDescription());
+ } catch (CloudRuntimeException e) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
}
}
@@ -134,11 +128,14 @@ public boolean isPublic() {
}
public String getVMName() {
- return getTargetVM().getInstanceName();
+ if (getName() == null) {
+ return getTargetVM().getInstanceName() + "-" + CLONE_IDENTIFIER;
+ }
+ return getName();
}
public String getTemplateName() {
- return getVMName() + "-QA";
+ return (getVMName() + "-" + _uuidMgr.generateUuid(VirtualMachineTemplate.class, null)).substring(0, 32);
}
@Override
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
index 733bddb45149..12a4a02c0264 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java
@@ -150,7 +150,9 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
+ "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call")
private String hypervisor;
- @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", length = 32768)
+ @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING,
+ description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding.",
+ length = 1048576)
private String userData;
@Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine")
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
index 38d1a5d5dd4b..38289537bc64 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
@@ -80,8 +80,13 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction,
@Parameter(name = ApiConstants.USER_DATA,
type = CommandType.STRING,
- description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.",
- length = 32768)
+ description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. " +
+ "This binary data must be base64 encoded before adding it to the request. " +
+ "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " +
+ "Using HTTP POST(via POST body), you can send up to 1MB of data after base64 encoding." +
+ "You also need to change vm.userdata.max.length value",
+ length = 1048576,
+ since = "4.16.0")
private String userData;
@Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin})
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java
index 140bdad717f7..33cbb46485c0 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java
@@ -30,6 +30,7 @@
import org.apache.cloudstack.context.CallContext;
import com.cloud.event.EventTypes;
+import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.VpnUser;
import com.cloud.user.Account;
@@ -110,19 +111,31 @@ public String getEventType() {
@Override
public void execute() {
Account owner = _accountService.getAccount(getEntityOwnerId());
- boolean result = _ravService.removeVpnUser(owner.getId(), userName, CallContext.current().getCallingAccount());
+ long ownerId = owner.getId();
+ boolean result = _ravService.removeVpnUser(ownerId, userName, CallContext.current().getCallingAccount());
if (!result) {
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove vpn user");
+ String errorMessage = String.format("Failed to remove VPN user=[%s]. VPN owner id=[%s].", userName, ownerId);
+ s_logger.error(errorMessage);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage);
}
+ boolean appliedVpnUsers = false;
+
try {
- if (!_ravService.applyVpnUsers(owner.getId(), userName)) {
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to apply vpn user removal");
- }
- }catch (Exception ex) {
- throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove vpn user due to resource unavailable");
+ appliedVpnUsers = _ravService.applyVpnUsers(ownerId, userName);
+ } catch (ResourceUnavailableException ex) {
+ String errorMessage = String.format("Failed to refresh VPN user=[%s] due to resource unavailable. VPN owner id=[%s].", userName, ownerId);
+ s_logger.error(errorMessage, ex);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage, ex);
+ }
+
+ if (!appliedVpnUsers) {
+ String errorMessage = String.format("Failed to refresh VPN user=[%s]. VPN owner id=[%s].", userName, ownerId);
+ s_logger.debug(errorMessage);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMessage);
}
+
SuccessResponse response = new SuccessResponse(getCommandName());
setResponseObject(response);
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
index a1337144a6ca..9464317f779b 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
@@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.response;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -246,6 +247,10 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes
@Param(description = "If the network has redundant routers enabled", since = "4.11.1")
private Boolean redundantRouter;
+ @SerializedName(ApiConstants.CREATED)
+ @Param(description = "the date this network was created", since = "4.16.0")
+ private Date created;
+
public Boolean getDisplayNetwork() {
return displayNetwork;
}
@@ -482,4 +487,12 @@ public String getVpcName() {
public void setVpcName(String vpcName) {
this.vpcName = vpcName;
}
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public void setCreated(Date created) {
+ this.created = created;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java
index 47ebab8756fc..7f14fce30078 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java
@@ -17,6 +17,7 @@
package org.apache.cloudstack.api.response;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -207,6 +208,10 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou
@Param(description = "the total number of virtual machines running for this project", since = "4.2.0")
private Integer vmRunning;
+ @SerializedName(ApiConstants.CREATED)
+ @Param(description = "the date this project was created", since = "4.16.0")
+ private Date created;
+
public void setId(String id) {
this.id = id;
}
@@ -421,4 +426,12 @@ public void setSecondaryStorageAvailable(String secondaryStorageAvailable) {
public void setOwners(List