[
https://issues.apache.org/jira/browse/CLOUDSTACK-10290?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16482315#comment-16482315
]
ASF GitHub Bot commented on CLOUDSTACK-10290:
---------------------------------------------
rhtyd closed pull request #2651: CLOUDSTACK-10290: Introduce option to have
config drives on primary storage
URL: https://github.com/apache/cloudstack/pull/2651
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java
b/api/src/com/cloud/network/element/UserDataServiceProvider.java
index 45ab0d877a3..bf6d7e819e9 100644
--- a/api/src/com/cloud/network/element/UserDataServiceProvider.java
+++ b/api/src/com/cloud/network/element/UserDataServiceProvider.java
@@ -26,7 +26,7 @@
import com.cloud.vm.VirtualMachineProfile;
public interface UserDataServiceProvider extends NetworkElement {
- public boolean addPasswordAndUserdata(Network network, NicProfile nic,
VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
+ boolean addPasswordAndUserdata(Network network, NicProfile nic,
VirtualMachineProfile vm, DeployDestination dest, ReservationContext context)
throws ConcurrentOperationException, InsufficientCapacityException,
ResourceUnavailableException;
boolean savePassword(Network network, NicProfile nic,
VirtualMachineProfile vm) throws ResourceUnavailableException;
diff --git a/client/pom.xml b/client/pom.xml
index 5653f536ac6..60402759576 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -378,6 +378,11 @@
<artifactId>cloud-engine-storage-cache</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine-storage-configdrive</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-controller-secondary-storage</artifactId>
diff --git a/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
b/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
index d6d87d48c05..3d8d8f7e10e 100644
--- a/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
+++ b/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
@@ -19,28 +19,22 @@
package com.cloud.agent.api;
-import java.util.List;
-
import com.cloud.agent.api.to.DataStoreTO;
public class HandleConfigDriveIsoCommand extends Command {
- String isoFile;
- List<String[]> vmData;
- String configDriveLabel;
- boolean create = false;
- private boolean update = false;
- private DataStoreTO destStore;
-
- public HandleConfigDriveIsoCommand(List<String[]> vmData, String label,
DataStoreTO destStore, String isoFile, boolean create, boolean update) {
- this.vmData = vmData;
- this.configDriveLabel = label;
- this.create = create;
- this.update = update;
- this.destStore = destStore;
+ @LogLevel(LogLevel.Log4jLevel.Off)
+ private String isoData;
+ private String isoFile;
+ private boolean create = false;
+ private DataStoreTO destStore;
+ public HandleConfigDriveIsoCommand(String isoFile, String isoData,
DataStoreTO destStore, boolean create) {
this.isoFile = isoFile;
+ this.isoData = isoData;
+ this.destStore = destStore;
+ this.create = create;
}
@Override
@@ -48,22 +42,14 @@ public boolean executeInSequence() {
return false;
}
- public List<String[]> getVmData() {
- return vmData;
- }
-
- public void setVmData(List<String[]> vmData) {
- this.vmData = vmData;
+ public String getIsoData() {
+ return isoData;
}
public boolean isCreate() {
return create;
}
- public String getConfigDriveLabel() {
- return configDriveLabel;
- }
-
public DataStoreTO getDestStore() {
return destStore;
}
@@ -71,8 +57,4 @@ public DataStoreTO getDestStore() {
public String getIsoFile() {
return isoFile;
}
-
- public boolean isUpdate() {
- return update;
- }
}
diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java
b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
index 556ec86f060..6041146b9de 100644
--- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
@@ -49,14 +49,17 @@
*/
public interface VirtualMachineManager extends Manager {
- static final ConfigKey<Boolean> ExecuteInSequence = new
ConfigKey<Boolean>("Advanced", Boolean.class,
"execute.in.sequence.hypervisor.commands", "false",
+ ConfigKey<Boolean> ExecuteInSequence = new ConfigKey<>("Advanced",
Boolean.class, "execute.in.sequence.hypervisor.commands", "false",
"If set to true, start, stop, reboot, copy and migrate commands
will be serialized on the agent side. If set to false the commands are executed
in parallel. Default value is false.", false);
- static final ConfigKey<String> VmConfigDriveLabel = new
ConfigKey<String>("Hidden", String.class, "vm.configdrive.label", "config-2",
+ ConfigKey<String> VmConfigDriveLabel = new ConfigKey<>("Hidden",
String.class, "vm.configdrive.label", "config-2",
"The default label name for the config drive", false);
- public interface Topics {
- public static final String VM_POWER_STATE = "vm.powerstate";
+ ConfigKey<Boolean> VmConfigDriveOnPrimaryPool = new
ConfigKey<>("Advanced", Boolean.class, "vm.configdrive.primarypool.enabled",
"false",
+ "If config drive need to be created and hosted on primary storage
pool. Currently only supported for KVM.", true);
+
+ interface Topics {
+ String VM_POWER_STATE = "vm.powerstate";
}
/**
diff --git
a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 8b3071fb695..6f2ae852e12 100755
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1108,10 +1108,11 @@ public void orchestrateStart(final String vmUuid, final
Map<VirtualMachineProfil
}
try {
- _networkMgr.prepare(vmProfile, new
DeployDestination(dest.getDataCenter(), dest.getPod(), null, null), ctx);
+ _networkMgr.prepare(vmProfile, new
DeployDestination(dest.getDataCenter(), dest.getPod(), null, null,
dest.getStorageForDisks()), ctx);
if (vm.getHypervisorType() != HypervisorType.BareMetal) {
volumeMgr.prepare(vmProfile, dest);
}
+
//since StorageMgr succeeded in volume creation, reuse
Volume for further tries until current cluster has capacity
if (!reuseVolume) {
reuseVolume = true;
@@ -4018,7 +4019,7 @@ public String getConfigComponentName() {
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {ClusterDeltaSyncInterval, StartRetry,
VmDestroyForcestop, VmOpCancelInterval, VmOpCleanupInterval, VmOpCleanupWait,
VmOpLockStateRetry,
- VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval,
VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel, HaVmRestartHostUp};
+ VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval,
VmJobTimeout, VmJobStateReportInterval, VmConfigDriveLabel,
VmConfigDriveOnPrimaryPool, HaVmRestartHostUp};
}
public List<StoragePoolAllocator> getStoragePoolAllocators() {
diff --git a/engine/pom.xml b/engine/pom.xml
index be86f1c9dc1..4fc66d12a60 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -53,6 +53,7 @@
<module>storage/datamotion</module>
<module>storage/cache</module>
<module>storage/snapshot</module>
+ <module>storage/configdrive</module>
<module>components-api</module>
<module>network</module>
<module>service</module>
diff --git a/engine/storage/configdrive/pom.xml
b/engine/storage/configdrive/pom.xml
new file mode 100644
index 00000000000..dc3d118eb68
--- /dev/null
+++ b/engine/storage/configdrive/pom.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>cloud-engine-storage-configdrive</artifactId>
+ <name>Apache CloudStack Framework - Storage Config Drive Component</name>
+ <parent>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine</artifactId>
+ <version>4.11.1.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java
b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java
new file mode 100644
index 00000000000..ec461991537
--- /dev/null
+++
b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDrive.java
@@ -0,0 +1,36 @@
+// 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.storage.configdrive;
+
+public class ConfigDrive {
+
+ public final static String CONFIGDRIVEFILENAME = "configdrive.iso";
+ public final static String CONFIGDRIVEDIR = "configdrive";
+
+ public static final String cloudStackConfigDriveName = "/cloudstack/";
+ public static final String openStackConfigDriveName = "/openstack/latest/";
+
+ /**
+ * This is the path to iso file relative to mount point
+ * @return config drive iso file path
+ */
+ public static String createConfigDrivePath(final String instanceName) {
+ return ConfigDrive.CONFIGDRIVEDIR + "/" + instanceName + "/" +
ConfigDrive.CONFIGDRIVEFILENAME;
+ }
+
+}
diff --git
a/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
new file mode 100644
index 00000000000..d847aa1d1d7
--- /dev/null
+++
b/engine/storage/configdrive/src/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilder.java
@@ -0,0 +1,222 @@
+// 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.storage.configdrive;
+
+import static com.cloud.network.NetworkModel.CONFIGDATA_CONTENT;
+import static com.cloud.network.NetworkModel.CONFIGDATA_DIR;
+import static com.cloud.network.NetworkModel.CONFIGDATA_FILE;
+import static com.cloud.network.NetworkModel.PASSWORD_FILE;
+import static com.cloud.network.NetworkModel.USERDATA_FILE;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.joda.time.Duration;
+
+import com.cloud.network.NetworkModel;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+import com.google.common.base.Strings;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+public class ConfigDriveBuilder {
+
+ public static final Logger LOG =
Logger.getLogger(ConfigDriveBuilder.class);
+
+ private static void writeFile(final File folder, final String file, final
String content) {
+ if (folder == null || Strings.isNullOrEmpty(file)) {
+ return;
+ }
+ final File vendorDataFile = new File(folder, file);
+ try (final FileWriter fw = new FileWriter(vendorDataFile); final
BufferedWriter bw = new BufferedWriter(fw)) {
+ bw.write(content);
+ } catch (IOException ex) {
+ throw new CloudRuntimeException("Failed to create config drive
file " + file, ex);
+ }
+ }
+
+ public static String fileToBase64String(final File isoFile) throws
IOException {
+ byte[] encoded =
Base64.encodeBase64(FileUtils.readFileToByteArray(isoFile));
+ return new String(encoded, StandardCharsets.US_ASCII);
+ }
+
+ public static File base64StringToFile(final String encodedIsoData, final
String folder, final String fileName) throws IOException {
+ byte[] decoded =
Base64.decodeBase64(encodedIsoData.getBytes(StandardCharsets.US_ASCII));
+ Path destPath = Paths.get(folder, fileName);
+ return Files.write(destPath, decoded).toFile();
+ }
+
+ public static String buildConfigDrive(final List<String[]> vmData, final
String isoFileName, final String driveLabel) {
+ if (vmData == null) {
+ throw new CloudRuntimeException("No VM metadata provided");
+ }
+
+ Path tempDir = null;
+ String tempDirName = null;
+ try {
+ tempDir = Files.createTempDirectory(ConfigDrive.CONFIGDRIVEDIR);
+ tempDirName = tempDir.toString();
+
+ File openStackFolder = new File(tempDirName +
ConfigDrive.openStackConfigDriveName);
+ if (openStackFolder.exists() || openStackFolder.mkdirs()) {
+ writeFile(openStackFolder, "vendor_data.json", "{}");
+ writeFile(openStackFolder, "network_data.json", "{}");
+ } else {
+ throw new CloudRuntimeException("Failed to create folder " +
openStackFolder);
+ }
+
+ JsonObject metaData = new JsonObject();
+ for (String[] item : vmData) {
+ String dataType = item[CONFIGDATA_DIR];
+ String fileName = item[CONFIGDATA_FILE];
+ String content = item[CONFIGDATA_CONTENT];
+ LOG.debug(String.format("[createConfigDriveIsoForVM]
dataType=%s, filename=%s, content=%s",
+ dataType, fileName,
(fileName.equals(PASSWORD_FILE)?"********":content)));
+
+ // create file with content in folder
+ if (dataType != null && !dataType.isEmpty()) {
+ //create folder
+ File typeFolder = new File(tempDirName +
ConfigDrive.cloudStackConfigDriveName + dataType);
+ if (typeFolder.exists() || typeFolder.mkdirs()) {
+ if (StringUtils.isNotEmpty(content)) {
+ File file = new File(typeFolder, fileName +
".txt");
+ try {
+ if (fileName.equals(USERDATA_FILE)) {
+ // User Data is passed as a base64 encoded
string
+ FileUtils.writeByteArrayToFile(file,
Base64.decodeBase64(content));
+ } else {
+ FileUtils.write(file, content,
com.cloud.utils.StringUtils.getPreferredCharset());
+ }
+ } catch (IOException ex) {
+ throw new CloudRuntimeException("Failed to
create file ", ex);
+ }
+ }
+ } else {
+ throw new CloudRuntimeException("Failed to create
folder: " + typeFolder);
+ }
+
+ //now write the file to the OpenStack directory
+ metaData = buildOpenStackMetaData(metaData, dataType,
fileName, content);
+ }
+ }
+ writeFile(openStackFolder, "meta_data.json", metaData.toString());
+
+ String linkResult = linkUserData(tempDirName);
+ if (linkResult != null) {
+ String errMsg = "Unable to create user_data link due to " +
linkResult;
+ throw new CloudRuntimeException(errMsg);
+ }
+
+ File tmpIsoStore = new File(tempDirName, new
File(isoFileName).getName());
+ Script command = new Script("/usr/bin/genisoimage",
Duration.standardSeconds(300), LOG);
+ command.add("-o", tmpIsoStore.getAbsolutePath());
+ command.add("-ldots");
+ command.add("-allow-lowercase");
+ command.add("-allow-multidot");
+ command.add("-cache-inodes"); // Enable caching inode and device
numbers to find hard links to files.
+ command.add("-l");
+ command.add("-quiet");
+ command.add("-J");
+ command.add("-r");
+ command.add("-V", driveLabel);
+ command.add(tempDirName);
+ LOG.debug("Executing config drive creation command: " +
command.toString());
+ String result = command.execute();
+ if (result != null) {
+ String errMsg = "Unable to create iso file: " + isoFileName +
" due to " + result;
+ LOG.warn(errMsg);
+ throw new CloudRuntimeException(errMsg);
+ }
+ File tmpIsoFile = new File(tmpIsoStore.getAbsolutePath());
+ // Max allowed file size of config drive is 64MB:
https://docs.openstack.org/project-install-guide/baremetal/draft/configdrive.html
+ if (tmpIsoFile.length() > (64L * 1024L * 1024L)) {
+ throw new CloudRuntimeException("Config drive file exceeds
maximum allowed size of 64MB");
+ }
+ return fileToBase64String(tmpIsoFile);
+ } catch (IOException e) {
+ throw new CloudRuntimeException("Failed due to", e);
+ } finally {
+ try {
+ FileUtils.deleteDirectory(tempDir.toFile());
+ } catch (IOException ioe) {
+ LOG.warn("Failed to delete ConfigDrive temporary directory: "
+ tempDirName, ioe);
+ }
+ }
+ }
+
+ private static String linkUserData(String tempDirName) {
+ //Hard link the user_data.txt file with the user_data file in the
OpenStack directory.
+ String userDataFilePath = tempDirName +
ConfigDrive.cloudStackConfigDriveName + "userdata/user_data.txt";
+ if ((new File(userDataFilePath).exists())) {
+ Script hardLink = new Script("ln", Duration.standardSeconds(300),
LOG);
+ hardLink.add(userDataFilePath);
+ hardLink.add(tempDirName + ConfigDrive.openStackConfigDriveName +
"user_data");
+ LOG.debug("execute command: " + hardLink.toString());
+ return hardLink.execute();
+ }
+ return null;
+ }
+
+ private static JsonArray arrayOf(JsonElement... elements) {
+ JsonArray array = new JsonArray();
+ for (JsonElement element : elements) {
+ array.add(element);
+ }
+ return array;
+ }
+
+ private static JsonObject buildOpenStackMetaData(JsonObject metaData,
String dataType, String fileName, String content) {
+ if (dataType.equals(NetworkModel.METATDATA_DIR) &&
StringUtils.isNotEmpty(content)) {
+ //keys are a special case in OpenStack format
+ if (NetworkModel.PUBLIC_KEYS_FILE.equals(fileName)) {
+ String[] keyArray = content.replace("\\n", "").split(" ");
+ String keyName = "key";
+ if (keyArray.length > 3 &&
StringUtils.isNotEmpty(keyArray[2])){
+ keyName = keyArray[2];
+ }
+
+ JsonObject keyLegacy = new JsonObject();
+ keyLegacy.addProperty("type", "ssh");
+ keyLegacy.addProperty("data", content.replace("\\n", ""));
+ keyLegacy.addProperty("name", keyName);
+ metaData.add("keys", arrayOf(keyLegacy));
+
+ JsonObject key = new JsonObject();
+ key.addProperty(keyName, content);
+ metaData.add("public_keys", key);
+ } else if (NetworkModel.openStackFileMapping.get(fileName) !=
null) {
+
metaData.addProperty(NetworkModel.openStackFileMapping.get(fileName), content);
+ }
+ }
+ return metaData;
+ }
+
+}
diff --git
a/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java
b/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java
new file mode 100644
index 00000000000..50a4384d5c8
--- /dev/null
+++
b/engine/storage/configdrive/test/org/apache/cloudstack/storage/configdrive/ConfigDriveBuilderTest.java
@@ -0,0 +1,65 @@
+// 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.storage.configdrive;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ConfigDriveBuilderTest {
+
+ @Test
+ public void testConfigDriveIsoPath() throws IOException {
+ Assert.assertEquals(ConfigDrive.createConfigDrivePath("i-x-y"),
"configdrive/i-x-y/configdrive.iso");
+ }
+
+ @Test
+ public void testConfigDriveBuild() throws IOException {
+ List<String[]> actualVmData = Arrays.asList(
+ new String[]{"userdata", "user_data", "c29tZSB1c2VyIGRhdGE="},
+ new String[]{"metadata", "service-offering", "offering"},
+ new String[]{"metadata", "availability-zone", "zone1"},
+ new String[]{"metadata", "local-hostname", "hostname"},
+ new String[]{"metadata", "local-ipv4", "192.168.111.111"},
+ new String[]{"metadata", "public-hostname", "7.7.7.7"},
+ new String[]{"metadata", "public-ipv4", "7.7.7.7"},
+ new String[]{"metadata", "vm-id", "uuid"},
+ new String[]{"metadata", "instance-id", "i-x-y"},
+ new String[]{"metadata", "public-keys", "ssh-rsa some-key"},
+ new String[]{"metadata", "cloud-identifier",
String.format("CloudStack-{%s}", "uuid")},
+ new String[]{"password", "vm_password", "password123"}
+ );
+
+ final Path tempDir =
Files.createTempDirectory(ConfigDrive.CONFIGDRIVEDIR);
+ final String isoData =
ConfigDriveBuilder.buildConfigDrive(actualVmData, "i-x-y.iso", "config-2");
+ final File isoFile = ConfigDriveBuilder.base64StringToFile(isoData,
tempDir.toAbsolutePath().toString(), ConfigDrive.CONFIGDRIVEFILENAME);
+
+ Assert.assertTrue(isoFile.exists());
+ Assert.assertTrue(isoFile.isFile());
+ Assert.assertTrue(isoFile.length() > 0L);
+
+ FileUtils.deleteDirectory(tempDir.toFile());
+ }
+}
\ No newline at end of file
diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec
index 525421c0108..96b679c8955 100644
--- a/packaging/centos7/cloud.spec
+++ b/packaging/centos7/cloud.spec
@@ -75,7 +75,7 @@ Requires: sudo
Requires: /sbin/service
Requires: /sbin/chkconfig
Requires: /usr/bin/ssh-keygen
-Requires: mkisofs
+Requires: genisoimage
Requires: mysql-connector-python
Requires: ipmitool
Requires: %{name}-common = %{_ver}
diff --git a/plugins/hypervisors/kvm/pom.xml b/plugins/hypervisors/kvm/pom.xml
index d90e79adfc9..1d0dd66cf87 100644
--- a/plugins/hypervisors/kvm/pom.xml
+++ b/plugins/hypervisors/kvm/pom.xml
@@ -39,6 +39,11 @@
<artifactId>cloud-plugin-network-ovs</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine-storage-configdrive</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>com.ceph</groupId>
<artifactId>rados</artifactId>
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 fc5e5395b87..424280cc66c 100644
---
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -2200,9 +2200,8 @@ public String getVolumePath(final Connect conn, final
DiskTO volume) throws Libv
final DataTO data = volume.getData();
final DataStoreTO store = data.getDataStore();
- if (volume.getType() == Volume.Type.ISO && data.getPath() != null) {
- final NfsTO nfsStore = (NfsTO)store;
- final String isoPath = nfsStore.getUrl() + File.separator +
data.getPath();
+ if (volume.getType() == Volume.Type.ISO && data.getPath() != null &&
(store instanceof NfsTO || store instanceof PrimaryDataStoreTO)) {
+ final String isoPath = store.getUrl().split("\\?")[0] +
File.separator + data.getPath();
final int index = isoPath.lastIndexOf("/");
final String path = isoPath.substring(0, index);
final String name = isoPath.substring(index + 1);
diff --git
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtHandleConfigDriveCommandWrapper.java
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtHandleConfigDriveCommandWrapper.java
new file mode 100644
index 00000000000..5e4ef485123
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtHandleConfigDriveCommandWrapper.java
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package com.cloud.hypervisor.kvm.resource.wrapper;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder;
+import org.apache.commons.io.FileUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.HandleConfigDriveIsoCommand;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.storage.Storage;
+
+@ResourceWrapper(handles = HandleConfigDriveIsoCommand.class)
+public final class LibvirtHandleConfigDriveCommandWrapper extends
CommandWrapper<HandleConfigDriveIsoCommand, Answer, LibvirtComputingResource> {
+ private static final Logger LOG =
Logger.getLogger(LibvirtHandleConfigDriveCommandWrapper.class);
+
+ @Override
+ public Answer execute(final HandleConfigDriveIsoCommand command, final
LibvirtComputingResource libvirtComputingResource) {
+ final KVMStoragePoolManager storagePoolMgr =
libvirtComputingResource.getStoragePoolMgr();
+ final KVMStoragePool pool =
storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem,
command.getDestStore().getUuid());
+ if (pool == null) {
+ return new Answer(command, false, "Pool not found, config drive
for KVM is only supported for NFS");
+ }
+
+ final String mountPoint = pool.getLocalPath();
+ final Path isoPath = Paths.get(mountPoint, command.getIsoFile());
+ final File isoFile = new File(mountPoint, command.getIsoFile());
+ if (command.isCreate()) {
+ LOG.debug("Creating config drive: " + command.getIsoFile());
+ if (command.getIsoData() == null) {
+ return new Answer(command, false, "Invalid config drive ISO
data received");
+ }
+ if (isoFile.exists()) {
+ LOG.debug("An old config drive iso already exists");
+ }
+ try {
+ Files.createDirectories(isoPath.getParent());
+ ConfigDriveBuilder.base64StringToFile(command.getIsoData(),
mountPoint, command.getIsoFile());
+ } catch (IOException e) {
+ return new Answer(command, false, "Failed due to exception: "
+ e.getMessage());
+ }
+ } else {
+ try {
+ FileUtils.deleteDirectory(isoPath.getParent().toFile());
+ } catch (IOException e) {
+ LOG.warn("Failed to delete config drive: " +
isoPath.toAbsolutePath().toString());
+ return new Answer(command, false, "Failed due to exception: "
+ e.getMessage());
+ }
+ }
+
+ return new Answer(command);
+ }
+}
\ No newline at end of file
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 c6135080671..4d0523c9a06 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
@@ -265,7 +265,7 @@ public KVMStoragePool getStoragePoolByURI(String uri) {
String uuid = null;
String sourceHost = "";
StoragePoolType protocol = null;
- if (storageUri.getScheme().equalsIgnoreCase("nfs")) {
+ if (storageUri.getScheme().equalsIgnoreCase("nfs") ||
storageUri.getScheme().equalsIgnoreCase("NetworkFilesystem")) {
sourcePath = storageUri.getPath();
sourcePath = sourcePath.replace("//", "/");
sourceHost = storageUri.getHost();
diff --git a/server/pom.xml b/server/pom.xml
index 534b1a34c70..55d0a6e247f 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -141,6 +141,11 @@
<artifactId>cloud-framework-agent-lb</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine-storage-configdrive</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
diff --git
a/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
b/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
index 585b443557b..dab4a14311c 100644
--- a/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
+++ b/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
@@ -20,20 +20,20 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.inject.Inject;
-import org.apache.log4j.Logger;
+import javax.inject.Inject;
-import
org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
+import org.apache.cloudstack.storage.configdrive.ConfigDrive;
+import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
+import org.apache.log4j.Logger;
+
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.AttachIsoAnswer;
-import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.configuration.ConfigurationManager;
@@ -41,10 +41,8 @@
import com.cloud.deploy.DeployDestination;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
-import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.UnsupportedServiceException;
-import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.network.Network;
import com.cloud.network.Network.Capability;
@@ -54,54 +52,48 @@
import com.cloud.network.NetworkModel;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.PhysicalNetworkServiceProvider;
-import com.cloud.network.dao.NetworkDao;
import com.cloud.offering.NetworkOffering;
import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage;
+import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.component.AdapterBase;
+import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.StateListener;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.Nic;
import com.cloud.vm.NicProfile;
import com.cloud.vm.ReservationContext;
import com.cloud.vm.UserVmDetailVO;
-import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.VirtualMachineProfile;
-import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
public class ConfigDriveNetworkElement extends AdapterBase implements
NetworkElement, UserDataServiceProvider,
StateListener<VirtualMachine.State, VirtualMachine.Event,
VirtualMachine>, NetworkMigrationResponder {
- private static final Logger s_logger =
Logger.getLogger(ConfigDriveNetworkElement.class);
+ private static final Logger LOG =
Logger.getLogger(ConfigDriveNetworkElement.class);
private static final Map<Service, Map<Capability, String>> capabilities =
setCapabilities();
- @Inject
- NetworkDao _networkConfigDao;
@Inject
NetworkModel _networkMgr;
@Inject
- UserVmManager _userVmMgr;
- @Inject
UserVmDao _userVmDao;
@Inject
UserVmDetailsDao _userVmDetailsDao;
@Inject
- DomainRouterDao _routerDao;
- @Inject
ConfigurationManager _configMgr;
@Inject
DataCenterDao _dcDao;
@Inject
- AgentManager _agentManager;
- @Inject
ServiceOfferingDao _serviceOfferingDao;
@Inject
NetworkModel _networkModel;
@@ -110,16 +102,16 @@
@Inject
GuestOSDao _guestOSDao;
@Inject
+ VolumeDao _volumeDao;
+ @Inject
HostDao _hostDao;
@Inject
+ AgentManager agentManager;
+ @Inject
DataStoreManager _dataStoreMgr;
@Inject
EndPointSelector _ep;
- @Inject
- VolumeOrchestrationService _volumeMgr;
- private final static String CONFIGDRIVEFILENAME = "configdrive.iso";
- private final static String CONFIGDRIVEDIR = "ConfigDrive";
private final static Integer CONFIGDRIVEDISKSEQ = 4;
private boolean canHandle(TrafficType trafficType) {
@@ -149,20 +141,13 @@ public boolean release(Network network, NicProfile nic,
VirtualMachineProfile vm
if (!nic.isDefaultNic()) {
return true;
}
- // Remove form secondary storage
- DataStore secondaryStore =
_dataStoreMgr.getImageStore(network.getDataCenterId());
-
- String isoFile = "/" + CONFIGDRIVEDIR + "/" + vm.getInstanceName()+
"/" + CONFIGDRIVEFILENAME;
- HandleConfigDriveIsoCommand deleteCommand = new
HandleConfigDriveIsoCommand(vm.getVmData(),
- vm.getConfigDriveLabel(), secondaryStore.getTO(), isoFile,
false, false);
- // Delete the ISO on the secondary store
- EndPoint endpoint = _ep.select(secondaryStore);
- if (endpoint == null) {
- s_logger.error(String.format("Secondary store: %s not available",
secondaryStore.getName()));
+
+ try {
+ return deleteConfigDriveIso(vm.getVirtualMachine());
+ } catch (ResourceUnavailableException e) {
+ LOG.error("Failed to delete config drive due to: ", e);
return false;
}
- Answer answer = endpoint.sendMessage(deleteCommand);
- return answer.getResult();
}
@Override
@@ -207,37 +192,49 @@ public boolean canEnableIndividualServices() {
}
private String getSshKey(VirtualMachineProfile profile) {
- UserVmDetailVO vmDetailSshKey =
_userVmDetailsDao.findDetail(profile.getId(), "SSH.PublicKey");
+ final UserVmDetailVO vmDetailSshKey =
_userVmDetailsDao.findDetail(profile.getId(), "SSH.PublicKey");
return (vmDetailSshKey!=null ? vmDetailSshKey.getValue() : null);
}
@Override
public boolean addPasswordAndUserdata(Network network, NicProfile nic,
VirtualMachineProfile profile, DeployDestination dest, ReservationContext
context)
throws ConcurrentOperationException,
InsufficientCapacityException, ResourceUnavailableException {
- String sshPublicKey = getSshKey(profile);
return (canHandle(network.getTrafficType())
- && updateConfigDrive(profile, sshPublicKey, nic))
- && updateConfigDriveIso(network, profile, dest.getHost(),
false);
+ && configureConfigDriveData(profile, nic))
+ && createConfigDriveIso(profile, dest);
}
@Override
- public boolean savePassword(Network network, NicProfile nic,
VirtualMachineProfile profile) throws ResourceUnavailableException {
- String sshPublicKey = getSshKey(profile);
- if (!(canHandle(network.getTrafficType()) &&
updateConfigDrive(profile, sshPublicKey, nic))) return false;
- return updateConfigDriveIso(network, profile, true);
+ public boolean savePassword(final Network network, final NicProfile nic,
final VirtualMachineProfile vm) throws ResourceUnavailableException {
+ // savePassword is called by resetPasswordForVirtualMachine API which
requires VM to be shutdown
+ // Upper layers should save password in db, we do not need to
update/create config drive iso at this point
+ // Config drive will be created with updated password when VM starts
in future
+ if (vm != null &&
vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
+ throw new CloudRuntimeException("VM should to stopped to reset
password");
+ }
+ return canHandle(network.getTrafficType());
}
@Override
- public boolean saveSSHKey(Network network, NicProfile nic,
VirtualMachineProfile vm, String sshPublicKey) throws
ResourceUnavailableException {
- if (!(canHandle(network.getTrafficType()) && updateConfigDrive(vm,
sshPublicKey, nic))) return false;
- return updateConfigDriveIso(network, vm, true);
+ public boolean saveSSHKey(final Network network, final NicProfile nic,
final VirtualMachineProfile vm, final String sshPublicKey) throws
ResourceUnavailableException {
+ // saveSSHKey is called by resetSSHKeyForVirtualMachine API which
requires VM to be shutdown
+ // Upper layers should save ssh public key in db, we do not need to
update/create config drive iso at this point
+ // Config drive will be created with updated password when VM starts
in future
+ if (vm != null &&
vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
+ throw new CloudRuntimeException("VM should to stopped to reset
password");
+ }
+ return canHandle(network.getTrafficType());
}
@Override
- public boolean saveUserData(Network network, NicProfile nic,
VirtualMachineProfile profile) throws ResourceUnavailableException {
- String sshPublicKey = getSshKey(profile);
- if (!(canHandle(network.getTrafficType()) &&
updateConfigDrive(profile, sshPublicKey, nic))) return false;
- return updateConfigDriveIso(network, profile, true);
+ public boolean saveUserData(final Network network, final NicProfile nic,
final VirtualMachineProfile vm) throws ResourceUnavailableException {
+ // saveUserData is called by updateVirtualMachine API which requires
VM to be shutdown
+ // Upper layers should save userdata in db, we do not need to
update/create config drive iso at this point
+ // Config drive will be created with updated password when VM starts
in future
+ if (vm != null &&
vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
+ throw new CloudRuntimeException("VM should to stopped to reset
password");
+ }
+ return canHandle(network.getTrafficType());
}
@Override
@@ -251,30 +248,22 @@ public boolean
preStateTransitionEvent(VirtualMachine.State oldState, VirtualMac
}
@Override
- public boolean
postStateTransitionEvent(StateMachine2.Transition<VirtualMachine.State,
VirtualMachine.Event> transition, VirtualMachine vo, boolean status, Object
opaque) {
+ public boolean
postStateTransitionEvent(StateMachine2.Transition<VirtualMachine.State,
VirtualMachine.Event> transition, VirtualMachine vm, boolean status, Object
opaque) {
if (transition.getToState().equals(VirtualMachine.State.Expunging) &&
transition.getEvent().equals(VirtualMachine.Event.ExpungeOperation)) {
- Nic nic = _networkModel.getDefaultNic(vo.getId());
+ Nic nic = _networkModel.getDefaultNic(vm.getId());
+ if (nic == null) {
+ return true;
+ }
try {
- if (nic != null) {
- final Network network =
_networkMgr.getNetwork(nic.getNetworkId());
- final UserDataServiceProvider userDataUpdateProvider =
_networkModel.getUserDataUpdateProvider(network);
- final Provider provider =
userDataUpdateProvider.getProvider();
- if (provider.equals(Provider.ConfigDrive)) {
- // Delete config drive ISO on destroy
- DataStore secondaryStore =
_dataStoreMgr.getImageStore(vo.getDataCenterId());
- String isoFile = "/" + CONFIGDRIVEDIR + "/" +
vo.getInstanceName() + "/" + CONFIGDRIVEFILENAME;
- HandleConfigDriveIsoCommand deleteCommand = new
HandleConfigDriveIsoCommand(null,
- null, secondaryStore.getTO(), isoFile, false,
false);
- EndPoint endpoint = _ep.select(secondaryStore);
- if (endpoint == null) {
- s_logger.error(String.format("Secondary store: %s
not available", secondaryStore.getName()));
- return false;
- }
- Answer answer = endpoint.sendMessage(deleteCommand);
- if (!answer.getResult()) {
- s_logger.error(String.format("Update ISO failed,
details: %s", answer.getDetails()));
- return false;
- }
+ final Network network =
_networkMgr.getNetwork(nic.getNetworkId());
+ final UserDataServiceProvider userDataUpdateProvider =
_networkModel.getUserDataUpdateProvider(network);
+ final Provider provider = userDataUpdateProvider.getProvider();
+ if (provider.equals(Provider.ConfigDrive)) {
+ try {
+ return deleteConfigDriveIso(vm);
+ } catch (ResourceUnavailableException e) {
+ LOG.error("Failed to delete config drive due to: ", e);
+ return false;
}
}
} catch (UnsupportedServiceException usse) {}
@@ -285,9 +274,9 @@ public boolean
postStateTransitionEvent(StateMachine2.Transition<VirtualMachine.
@Override
public boolean prepareMigration(NicProfile nic, Network network,
VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) {
if (nic.isDefaultNic() &&
_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive))
{
- s_logger.trace(String.format("[prepareMigration] for vm: %s",
vm.getInstanceName()));
- DataStore secondaryStore =
_dataStoreMgr.getImageStore(network.getDataCenterId());
- configureConfigDriveDisk(vm, secondaryStore);
+ LOG.trace(String.format("[prepareMigration] for vm: %s",
vm.getInstanceName()));
+ final DataStore dataStore = findDataStore(vm, dest);
+ addConfigDriveDisk(vm, dataStore);
return false;
}
else return true;
@@ -295,64 +284,114 @@ public boolean prepareMigration(NicProfile nic, Network
network, VirtualMachineP
@Override
public void rollbackMigration(NicProfile nic, Network network,
VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
-
}
@Override
public void commitMigration(NicProfile nic, Network network,
VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
+ }
+ private DataStore findDataStore(VirtualMachineProfile profile,
DeployDestination dest) {
+ DataStore dataStore = null;
+ if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) {
+ if (dest.getStorageForDisks() != null) {
+ for (final Volume volume : dest.getStorageForDisks().keySet())
{
+ if (volume.getVolumeType() == Volume.Type.ROOT) {
+ final StoragePool primaryPool =
dest.getStorageForDisks().get(volume);
+ dataStore =
_dataStoreMgr.getDataStore(primaryPool.getId(), DataStoreRole.Primary);
+ break;
+ }
+ }
+ }
+ if (dataStore == null) {
+ final List<VolumeVO> volumes =
_volumeDao.findByInstanceAndType(profile.getVirtualMachine().getId(),
Volume.Type.ROOT);
+ if (volumes != null && volumes.size() > 0) {
+ dataStore =
_dataStoreMgr.getDataStore(volumes.get(0).getPoolId(), DataStoreRole.Primary);
+ }
+ }
+ } else {
+ dataStore =
_dataStoreMgr.getImageStore(dest.getDataCenter().getId());
+ }
+ return dataStore;
}
- private boolean updateConfigDriveIso(Network network,
VirtualMachineProfile profile, boolean update) throws
ResourceUnavailableException {
- return updateConfigDriveIso(network, profile, null, update);
+ private Long findAgentIdForImageStore(final DataStore dataStore) throws
ResourceUnavailableException {
+ EndPoint endpoint = _ep.select(dataStore);
+ if (endpoint == null) {
+ throw new ResourceUnavailableException("Config drive creation
failed, secondary store not available",
+ dataStore.getClass(), dataStore.getId());
+ }
+ return endpoint.getId();
}
- private boolean updateConfigDriveIso(Network network,
VirtualMachineProfile profile, Host host, boolean update) throws
ResourceUnavailableException {
- Integer deviceKey = null;
- Long hostId;
- if (host == null) {
- hostId = (profile.getVirtualMachine().getHostId() == null ?
profile.getVirtualMachine().getLastHostId():
profile.getVirtualMachine().getHostId());
+ private Long findAgentId(VirtualMachineProfile profile, DeployDestination
dest, DataStore dataStore) throws ResourceUnavailableException {
+ Long agentId;
+ if (dest.getHost() == null) {
+ agentId = (profile.getVirtualMachine().getHostId() == null ?
profile.getVirtualMachine().getLastHostId() :
profile.getVirtualMachine().getHostId());
} else {
- hostId = host.getId();
+ agentId = dest.getHost().getId();
+ }
+ if (!VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) {
+ agentId = findAgentIdForImageStore(dataStore);
}
+ return agentId;
+ }
- DataStore secondaryStore =
_dataStoreMgr.getImageStore(network.getDataCenterId());
- // Detach the existing ISO file if the machine is running
- if (update &&
profile.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
- s_logger.debug("Detach config drive ISO for vm " +
profile.getInstanceName() + " in host " + _hostDao.findById(hostId));
- deviceKey = detachIso(secondaryStore, profile.getInstanceName(),
hostId);
+ private boolean createConfigDriveIso(VirtualMachineProfile profile,
DeployDestination dest) throws ResourceUnavailableException {
+ final DataStore dataStore = findDataStore(profile, dest);
+ final Long agentId = findAgentId(profile, dest, dataStore);
+ if (agentId == null || dataStore == null) {
+ throw new ResourceUnavailableException("Config drive iso creation
failed, agent or datastore not available",
+ ConfigDriveNetworkElement.class, 0L);
}
- // Create/Update the iso on the secondary store
- s_logger.debug(String.format("%s config drive ISO for vm %s in host
%s",
- (update?"update":"create"), profile.getInstanceName(),
_hostDao.findById(hostId).getName()));
- EndPoint endpoint = _ep.select(secondaryStore);
- if (endpoint == null) {
- throw new ResourceUnavailableException(String.format("%s failed,
secondary store not available", (update ? "Update" : "Create")),
secondaryStore.getClass(),
- secondaryStore.getId());
+ LOG.debug("Creating config drive ISO for vm: " +
profile.getInstanceName());
+
+ final String isoPath =
ConfigDrive.createConfigDrivePath(profile.getInstanceName());
+ final String isoData =
ConfigDriveBuilder.buildConfigDrive(profile.getVmData(),
ConfigDrive.CONFIGDRIVEFILENAME, profile.getConfigDriveLabel());
+ final HandleConfigDriveIsoCommand configDriveIsoCommand = new
HandleConfigDriveIsoCommand(isoPath, isoData, dataStore.getTO(), true);
+
+ final Answer answer = agentManager.easySend(agentId,
configDriveIsoCommand);
+ if (!answer.getResult()) {
+ throw new ResourceUnavailableException(String.format("Config drive
iso creation failed, details: %s",
+ answer.getDetails()), ConfigDriveNetworkElement.class, 0L);
+ }
+ addConfigDriveDisk(profile, dataStore);
+ return true;
+ }
+
+ private boolean deleteConfigDriveIso(final VirtualMachine vm) throws
ResourceUnavailableException {
+ DataStore dataStore =
_dataStoreMgr.getImageStore(vm.getDataCenterId());
+ Long agentId = findAgentIdForImageStore(dataStore);
+
+ if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) {
+ List<VolumeVO> volumes =
_volumeDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
+ if (volumes != null && volumes.size() > 0) {
+ dataStore =
_dataStoreMgr.getDataStore(volumes.get(0).getPoolId(), DataStoreRole.Primary);
+ }
+ agentId = (vm.getHostId() != null) ? vm.getHostId() :
vm.getLastHostId();
}
- String isoPath = CONFIGDRIVEDIR + "/" + profile.getInstanceName() +
"/" + CONFIGDRIVEFILENAME;
- HandleConfigDriveIsoCommand configDriveIsoCommand = new
HandleConfigDriveIsoCommand(profile.getVmData(),
- profile.getConfigDriveLabel(), secondaryStore.getTO(),
isoPath, true, update);
- Answer createIsoAnswer = endpoint.sendMessage(configDriveIsoCommand);
- if (!createIsoAnswer.getResult()) {
- throw new ResourceUnavailableException(String.format("%s ISO
failed, details: %s",
- (update?"Update":"Create"), createIsoAnswer.getDetails()),
ConfigDriveNetworkElement.class, 0L);
+
+ if (agentId == null || dataStore == null) {
+ throw new ResourceUnavailableException("Config drive iso creation
failed, agent or datastore not available",
+ ConfigDriveNetworkElement.class, 0L);
}
- configureConfigDriveDisk(profile, secondaryStore);
- // Re-attach the ISO if the machine is running
- if (update &&
profile.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) {
- s_logger.debug("Re-attach config drive ISO for vm " +
profile.getInstanceName() + " in host " + _hostDao.findById(hostId));
- attachIso(secondaryStore, profile.getInstanceName(), hostId,
deviceKey);
+ LOG.debug("Deleting config drive ISO for vm: " + vm.getInstanceName());
+
+ final String isoPath =
ConfigDrive.createConfigDrivePath(vm.getInstanceName());
+ final HandleConfigDriveIsoCommand configDriveIsoCommand = new
HandleConfigDriveIsoCommand(isoPath, null, dataStore.getTO(), false);
+
+ final Answer answer = agentManager.easySend(agentId,
configDriveIsoCommand);
+ if (!answer.getResult()) {
+ LOG.error("Failed to remove config drive for instance: " +
vm.getInstanceName());
+ return false;
}
return true;
-
}
- private void configureConfigDriveDisk(VirtualMachineProfile profile,
DataStore secondaryStore) {
+ private void addConfigDriveDisk(final VirtualMachineProfile profile, final
DataStore dataStore) {
boolean isoAvailable = false;
- String isoPath = CONFIGDRIVEDIR + "/" + profile.getInstanceName() +
"/" + CONFIGDRIVEFILENAME;
+ final String isoPath =
ConfigDrive.createConfigDrivePath(profile.getInstanceName());
for (DiskTO dataTo : profile.getDisks()) {
if (dataTo.getPath().equals(isoPath)) {
isoAvailable = true;
@@ -361,71 +400,34 @@ private void
configureConfigDriveDisk(VirtualMachineProfile profile, DataStore s
}
if (!isoAvailable) {
TemplateObjectTO dataTO = new TemplateObjectTO();
- dataTO.setDataStore(secondaryStore.getTO());
+ dataTO.setDataStore(dataStore.getTO());
dataTO.setUuid(profile.getUuid());
dataTO.setPath(isoPath);
dataTO.setFormat(Storage.ImageFormat.ISO);
profile.addDisk(new DiskTO(dataTO, CONFIGDRIVEDISKSEQ.longValue(),
isoPath, Volume.Type.ISO));
+ } else {
+ LOG.warn("Config drive iso already is in VM profile.");
}
}
- private boolean updateConfigDrive(VirtualMachineProfile profile, String
publicKey, NicProfile nic) {
- UserVmVO vm = _userVmDao.findById(profile.getId());
+ private boolean configureConfigDriveData(final VirtualMachineProfile
profile, final NicProfile nic) {
+ final UserVmVO vm = _userVmDao.findById(profile.getId());
if (vm.getType() != VirtualMachine.Type.User) {
return false;
}
- // add/update userdata and/or password info into vm profile
- Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
+ final Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
if (defaultNic != null) {
+ final String sshPublicKey = getSshKey(profile);
final String serviceOffering =
_serviceOfferingDao.findByIdIncludingRemoved(vm.getId(),
vm.getServiceOfferingId()).getDisplayText();
boolean isWindows =
_guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
- List<String[]> vmData =
_networkModel.generateVmData(vm.getUserData(), serviceOffering,
vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(),
- vm.getUuid(), nic.getIPv4Address(), publicKey, (String)
profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
+ final List<String[]> vmData =
_networkModel.generateVmData(vm.getUserData(), serviceOffering,
vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(),
+ vm.getUuid(), nic.getIPv4Address(), sshPublicKey, (String)
profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows);
profile.setVmData(vmData);
profile.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value());
}
return true;
}
- private Integer detachIso (DataStore secondaryStore, String instanceName,
Long hostId) throws ResourceUnavailableException {
- String isoPath = CONFIGDRIVEDIR + "/" + instanceName + "/" +
CONFIGDRIVEFILENAME;
- AttachIsoCommand isoCommand = new AttachIsoCommand(instanceName,
secondaryStore.getUri() + "/" + isoPath, false, CONFIGDRIVEDISKSEQ, true);
- isoCommand.setStoreUrl(secondaryStore.getUri());
- Answer attachIsoAnswer = null;
-
- try {
- attachIsoAnswer = _agentManager.send(hostId, isoCommand);
- } catch (OperationTimedoutException e) {
- throw new ResourceUnavailableException("Detach ISO failed: " +
e.getMessage(), ConfigDriveNetworkElement.class, 0L);
- }
-
- if (!attachIsoAnswer.getResult()) {
- throw new ResourceUnavailableException("Detach ISO failed: " +
attachIsoAnswer.getDetails(), ConfigDriveNetworkElement.class, 0L);
- }
-
- if (attachIsoAnswer instanceof AttachIsoAnswer) {
- return ((AttachIsoAnswer)attachIsoAnswer).getDeviceKey();
- } else {
- return CONFIGDRIVEDISKSEQ;
- }
- }
-
- private void attachIso (DataStore secondaryStore, String instanceName,
Long hostId, Integer deviceKey) throws ResourceUnavailableException {
- String isoPath = CONFIGDRIVEDIR + "/" + instanceName + "/" +
CONFIGDRIVEFILENAME;
- AttachIsoCommand isoCommand = new AttachIsoCommand(instanceName,
secondaryStore.getUri() + "/" + isoPath, true);
- isoCommand.setStoreUrl(secondaryStore.getUri());
- isoCommand.setDeviceKey(deviceKey);
- Answer attachIsoAnswer = null;
- try {
- attachIsoAnswer = _agentManager.send(hostId, isoCommand);
- } catch (OperationTimedoutException e) {
- throw new ResourceUnavailableException("Attach ISO failed: " +
e.getMessage() ,ConfigDriveNetworkElement.class,0L);
- }
- if (!attachIsoAnswer.getResult()) {
- throw new ResourceUnavailableException("Attach ISO failed: " +
attachIsoAnswer.getDetails(),ConfigDriveNetworkElement.class,0L);
- }
- }
-
}
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java
b/server/src/com/cloud/vm/UserVmManagerImpl.java
index ecdd1125aab..4ac48c9cd31 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -653,10 +653,6 @@ protected void runInContext() {
}
}
-
-
-
-
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_RESETPASSWORD,
eventDescription = "resetting Vm password", async = true)
public UserVm resetVMPassword(ResetVMPasswordCmd cmd, String password)
throws ResourceUnavailableException, InsufficientCapacityException {
@@ -681,6 +677,11 @@ public UserVm resetVMPassword(ResetVMPasswordCmd cmd,
String password) throws Re
throw new InvalidParameterValueException("Vm with id " + vmId + "
is not in the right state");
}
+ if (userVm.getState() != State.Stopped) {
+ s_logger.error("vm is not in the right state: " + vmId);
+ throw new InvalidParameterValueException("Vm " + userVm + " should
be stopped to do password reset");
+ }
+
_accountMgr.checkAccess(caller, null, true, userVm);
boolean result = resetVMPasswordInternal(vmId, password);
@@ -733,6 +734,14 @@ private boolean resetVMPasswordInternal(Long vmId, String
password) throws Resou
s_logger.debug("Failed to reset password for the virtual
machine; no need to reboot the vm");
return false;
} else {
+ final UserVmVO userVm = _vmDao.findById(vmId);
+ _vmDao.loadDetails(userVm);
+ userVm.setPassword(password);
+ // update the password in vm_details table too
+ // Check if an SSH key pair was selected for the instance and
if so
+ // use it to encrypt & save the vm password
+ encryptAndStorePassword(userVm, password);
+
if (vmInstance.getState() == State.Stopped) {
s_logger.debug("Vm " + vmInstance + " is stopped, not
rebooting it as a part of password reset");
return true;
@@ -796,15 +805,7 @@ public UserVm resetVMSSHKey(ResetVMSSHKeyCmd cmd) throws
ResourceUnavailableExce
boolean result = resetVMSSHKeyInternal(vmId, sshPublicKey, password);
- if (result) {
- userVm.setDetail("SSH.PublicKey", sshPublicKey);
- if (template != null && template.getEnablePassword()) {
- userVm.setPassword(password);
- //update the encrypted password in vm_details table too
- encryptAndStorePassword(userVm, password);
- }
- _vmDao.saveDetails(userVm);
- } else {
+ if (!result) {
throw new CloudRuntimeException("Failed to reset SSH Key for the
virtual machine ");
}
return userVm;
@@ -842,6 +843,16 @@ private boolean resetVMSSHKeyInternal(Long vmId, String
sshPublicKey, String pas
s_logger.debug("Failed to reset SSH Key for the virtual machine;
no need to reboot the vm");
return false;
} else {
+ final UserVmVO userVm = _vmDao.findById(vmId);
+ _vmDao.loadDetails(userVm);
+ userVm.setDetail("SSH.PublicKey", sshPublicKey);
+ if (template.getEnablePassword()) {
+ userVm.setPassword(password);
+ //update the encrypted password in vm_details table too
+ encryptAndStorePassword(userVm, password);
+ }
+ _vmDao.saveDetails(userVm);
+
if (vmInstance.getState() == State.Stopped) {
s_logger.debug("Vm " + vmInstance + " is stopped, not
rebooting it as a part of SSH Key reset");
return true;
@@ -4099,8 +4110,6 @@ public boolean
finalizeVirtualMachineProfile(VirtualMachineProfile profile, Depl
}
}
-
-
_templateMgr.prepareIsoForVmProfile(profile, dest);
return true;
}
@@ -6135,14 +6144,7 @@ public UserVm restoreVMInternal(Account caller, UserVmVO
vm, Long newTemplateId)
if (template.getEnablePassword()) {
password = _mgr.generateRandomPassword();
boolean result = resetVMPasswordInternal(vmId, password);
- if (result) {
- vm.setPassword(password);
- _vmDao.loadDetails(vm);
- // update the password in vm_details table too
- // Check if an SSH key pair was selected for the instance
and if so
- // use it to encrypt & save the vm password
- encryptAndStorePassword(vm, password);
- } else {
+ if (!result) {
throw new CloudRuntimeException("VM reset is completed but
failed to reset password for the virtual machine ");
}
}
diff --git
a/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java
b/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java
index ff3f40b2645..e964999ee9d 100644
--- a/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java
+++ b/server/test/com/cloud/network/element/ConfigDriveNetworkElementTest.java
@@ -16,7 +16,6 @@
// under the License.
package com.cloud.network.element;
-import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
@@ -36,6 +35,12 @@
import java.util.List;
import java.util.Map;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
+import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.storage.configdrive.ConfigDrive;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
@@ -43,16 +48,10 @@
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
-import com.google.common.collect.Maps;
-
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
-import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
-import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
-import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
-
+import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
+import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.deploy.DeployDestination;
@@ -89,6 +88,7 @@
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
+import com.google.common.collect.Maps;
public class ConfigDriveNetworkElementTest {
@@ -106,6 +106,7 @@
private final long SOID = 31L;
private final long HOSTID = NETWORK_ID;
+ @Mock private DataCenter dataCenter;
@Mock private ConfigurationDao _configDao;
@Mock private DataCenterDao _dcDao;
@Mock private DataStoreManager _dataStoreMgr;
@@ -134,6 +135,7 @@
@Mock private ServiceOfferingVO serviceOfferingVO;
@Mock private UserVmVO virtualMachine;
@Mock private IPAddressVO publicIp;
+ @Mock private AgentManager agentManager;
@InjectMocks private final ConfigDriveNetworkElement
_configDrivesNetworkElement = new ConfigDriveNetworkElement();
@InjectMocks @Spy private NetworkModelImpl _networkModel = new
NetworkModelImpl();
@@ -145,6 +147,7 @@ public void setUp() throws NoSuchFieldException,
IllegalAccessException {
_configDrivesNetworkElement._networkModel = _networkModel;
when(_dataStoreMgr.getImageStore(DATACENTERID)).thenReturn(dataStore);
+
when(_ep.select(dataStore)).thenReturn(endpoint);
when(_vmDao.findById(VMID)).thenReturn(virtualMachine);
when(_dcDao.findById(DATACENTERID)).thenReturn(dataCenterVO);
@@ -167,7 +170,9 @@ public void setUp() throws NoSuchFieldException,
IllegalAccessException {
when(virtualMachine.getInstanceName()).thenReturn(VMINSTANCENAME);
when(virtualMachine.getUserData()).thenReturn(VMUSERDATA);
when(virtualMachine.getHostName()).thenReturn(VMHOSTNAME);
+ when(dataCenter.getId()).thenReturn(DATACENTERID);
when(deployDestination.getHost()).thenReturn(hostVO);
+ when(deployDestination.getDataCenter()).thenReturn(dataCenter);
when(hostVO.getId()).thenReturn(HOSTID);
when(nic.isDefaultNic()).thenReturn(true);
when(nic.getNetworkId()).thenReturn(NETWORK_ID);
@@ -210,25 +215,23 @@ public void testExpunge() throws NoTransitionException,
NoSuchFieldException, Il
when(_vmInstanceDao.updateState(VirtualMachine.State.Stopped,
VirtualMachine.Event.ExpungeOperation, VirtualMachine.State.Expunging,
virtualMachine, null)).thenReturn(true);
final Answer answer = mock(Answer.class);
-
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
+ when(agentManager.easySend(anyLong(),
any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
stateMachine.transitTo(virtualMachine,
VirtualMachine.Event.ExpungeOperation, null, _vmInstanceDao);
ArgumentCaptor<HandleConfigDriveIsoCommand> commandCaptor =
ArgumentCaptor.forClass(HandleConfigDriveIsoCommand.class);
- verify(endpoint, times(1)).sendMessage(commandCaptor.capture());
+ verify(agentManager, times(1)).easySend(anyLong(),
commandCaptor.capture());
HandleConfigDriveIsoCommand deleteCommand = commandCaptor.getValue();
assertThat(deleteCommand.isCreate(), is(false));
- assertThat(deleteCommand.isUpdate(), is(false));
-
}
@Test
public void testRelease() {
final Answer answer = mock(Answer.class);
-
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
+ when(agentManager.easySend(anyLong(),
any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
VirtualMachineProfile profile = new
VirtualMachineProfileImpl(virtualMachine, null, serviceOfferingVO, null, null);
assertTrue(_configDrivesNetworkElement.release(network, nicp, profile,
null));
@@ -240,81 +243,20 @@ public void testGetCapabilities () {
}
@Test
- public void testAddPasswordAndUserdata() throws
InsufficientCapacityException, ResourceUnavailableException {
- List<String[]> actualVmData = getVmData();
-
- assertThat(actualVmData, containsInAnyOrder(
- new String[]{"userdata", "user_data", VMUSERDATA},
- new String[]{"metadata", "service-offering", VMOFFERING},
- new String[]{"metadata", "availability-zone", ZONENAME},
- new String[]{"metadata", "local-hostname", VMHOSTNAME},
- new String[]{"metadata", "local-ipv4", "192.168.111.111"},
- new String[]{"metadata", "public-hostname", null},
- new String[]{"metadata", "public-ipv4", "192.168.111.111"},
- new String[]{"metadata", "vm-id", String.valueOf(VMID)},
- new String[]{"metadata", "instance-id", VMINSTANCENAME},
- new String[]{"metadata", "public-keys", PUBLIC_KEY},
- new String[]{"metadata", "cloud-identifier",
String.format("CloudStack-{%s}", CLOUD_ID)},
- new String[]{PASSWORD, "vm_password", PASSWORD}
- ));
- }
-
- @Test
- public void testAddPasswordAndUserdataStaticNat() throws
InsufficientCapacityException, ResourceUnavailableException {
- when(_ipAddressDao.findByAssociatedVmId(VMID)).thenReturn(publicIp);
- when(publicIp.getAddress()).thenReturn(new Ip("7.7.7.7"));
-
- List<String[]> actualVmData = getVmData();
-
- assertThat(actualVmData, containsInAnyOrder(
- new String[]{"userdata", "user_data", VMUSERDATA},
- new String[]{"metadata", "service-offering", VMOFFERING},
- new String[]{"metadata", "availability-zone", ZONENAME},
- new String[]{"metadata", "local-hostname", VMHOSTNAME},
- new String[]{"metadata", "local-ipv4", "192.168.111.111"},
- new String[]{"metadata", "public-hostname", "7.7.7.7"},
- new String[]{"metadata", "public-ipv4", "7.7.7.7"},
- new String[]{"metadata", "vm-id", String.valueOf(VMID)},
- new String[]{"metadata", "instance-id", VMINSTANCENAME},
- new String[]{"metadata", "public-keys", PUBLIC_KEY},
- new String[]{"metadata", "cloud-identifier",
String.format("CloudStack-{%s}", CLOUD_ID)},
- new String[]{PASSWORD, "vm_password", PASSWORD}
- ));
- }
-
-
- @Test
- public void testAddPasswordAndUserdataUuid() throws
InsufficientCapacityException, ResourceUnavailableException {
- when(virtualMachine.getUuid()).thenReturn("vm-uuid");
-
- List<String[]> actualVmData = getVmData();
-
- assertThat(actualVmData, containsInAnyOrder(
- new String[]{"userdata", "user_data", VMUSERDATA},
- new String[]{"metadata", "service-offering", VMOFFERING},
- new String[]{"metadata", "availability-zone", ZONENAME},
- new String[]{"metadata", "local-hostname", VMHOSTNAME},
- new String[]{"metadata", "local-ipv4", "192.168.111.111"},
- new String[]{"metadata", "public-hostname", null},
- new String[]{"metadata", "public-ipv4", "192.168.111.111"},
- new String[]{"metadata", "vm-id", "vm-uuid"},
- new String[]{"metadata", "instance-id", "vm-uuid"},
- new String[]{"metadata", "public-keys", PUBLIC_KEY},
- new String[]{"metadata", "cloud-identifier",
String.format("CloudStack-{%s}", CLOUD_ID)},
- new String[]{PASSWORD, "vm_password", PASSWORD}
- ));
- }
-
- private List<String[]> getVmData() throws InsufficientCapacityException,
ResourceUnavailableException {
+ public void testAddPasswordAndUserData() throws Exception {
final Answer answer = mock(Answer.class);
final UserVmDetailVO userVmDetailVO = mock(UserVmDetailVO.class);
-
when(endpoint.sendMessage(any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
+ when(agentManager.easySend(anyLong(),
any(HandleConfigDriveIsoCommand.class))).thenReturn(answer);
when(answer.getResult()).thenReturn(true);
when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest);
when(virtualMachine.getState()).thenReturn(VirtualMachine.State.Stopped);
+ when(virtualMachine.getUuid()).thenReturn("vm-uuid");
when(userVmDetailVO.getValue()).thenReturn(PUBLIC_KEY);
when(nicp.getIPv4Address()).thenReturn("192.168.111.111");
when(_userVmDetailsDao.findDetail(anyLong(),
anyString())).thenReturn(userVmDetailVO);
+ when(_ipAddressDao.findByAssociatedVmId(VMID)).thenReturn(publicIp);
+ when(publicIp.getAddress()).thenReturn(new Ip("7.7.7.7"));
+
Map<VirtualMachineProfile.Param, Object> parms = Maps.newHashMap();
parms.put(VirtualMachineProfile.Param.VmPassword, PASSWORD);
parms.put(VirtualMachineProfile.Param.VmSshPubKey, PUBLIC_KEY);
@@ -323,8 +265,11 @@ public void testAddPasswordAndUserdataUuid() throws
InsufficientCapacityExceptio
network, nicp, profile, deployDestination, null));
ArgumentCaptor<HandleConfigDriveIsoCommand> commandCaptor =
ArgumentCaptor.forClass(HandleConfigDriveIsoCommand.class);
- verify(endpoint, times(1)).sendMessage(commandCaptor.capture());
- HandleConfigDriveIsoCommand result = commandCaptor.getValue();
- return result.getVmData();
+ verify(agentManager, times(1)).easySend(anyLong(),
commandCaptor.capture());
+ HandleConfigDriveIsoCommand createCommand = commandCaptor.getValue();
+
+ assertTrue(createCommand.isCreate());
+ assertTrue(createCommand.getIsoData().length() > 0);
+
assertTrue(createCommand.getIsoFile().equals(ConfigDrive.createConfigDrivePath(profile.getInstanceName())));
}
}
diff --git a/services/secondary-storage/server/pom.xml
b/services/secondary-storage/server/pom.xml
index 7c7cf9b68bf..87ac1866180 100644
--- a/services/secondary-storage/server/pom.xml
+++ b/services/secondary-storage/server/pom.xml
@@ -50,6 +50,11 @@
<artifactId>cloud-server</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-engine-storage-configdrive</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- dependencies for starting a post upload server on ssvm -->
<dependency>
<groupId>io.netty</groupId>
diff --git
a/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
b/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
index da3ab32e203..125512bba91 100644
---
a/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
+++
b/services/secondary-storage/server/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
@@ -16,9 +16,6 @@
// under the License.
package org.apache.cloudstack.storage.resource;
-import static com.cloud.network.NetworkModel.CONFIGDATA_CONTENT;
-import static com.cloud.network.NetworkModel.CONFIGDATA_DIR;
-import static com.cloud.network.NetworkModel.CONFIGDATA_FILE;
import static com.cloud.network.NetworkModel.METATDATA_DIR;
import static com.cloud.network.NetworkModel.PASSWORD_DIR;
import static com.cloud.network.NetworkModel.PASSWORD_FILE;
@@ -45,7 +42,6 @@
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
-import java.nio.charset.Charset;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -56,46 +52,6 @@
import javax.naming.ConfigurationException;
-import io.netty.bootstrap.ServerBootstrap;
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelInitializer;
-import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
-import io.netty.channel.socket.nio.NioServerSocketChannel;
-import io.netty.handler.codec.http.HttpContentCompressor;
-import io.netty.handler.codec.http.HttpRequestDecoder;
-import io.netty.handler.codec.http.HttpResponseEncoder;
-import io.netty.handler.logging.LogLevel;
-import io.netty.handler.logging.LoggingHandler;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang.StringUtils;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
-import org.joda.time.format.ISODateTimeFormat;
-
-import com.amazonaws.services.s3.model.S3ObjectSummary;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.io.Files;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
import org.apache.cloudstack.framework.security.keystore.KeystoreManager;
import org.apache.cloudstack.storage.command.CopyCmdAnswer;
import org.apache.cloudstack.storage.command.CopyCommand;
@@ -106,6 +62,8 @@
import org.apache.cloudstack.storage.command.UploadStatusAnswer;
import org.apache.cloudstack.storage.command.UploadStatusAnswer.UploadStatus;
import org.apache.cloudstack.storage.command.UploadStatusCommand;
+import org.apache.cloudstack.storage.configdrive.ConfigDrive;
+import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder;
import org.apache.cloudstack.storage.template.DownloadManager;
import org.apache.cloudstack.storage.template.DownloadManagerImpl;
import
org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser;
@@ -117,7 +75,22 @@
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.cloudstack.utils.security.DigestHelper;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
+import org.joda.time.format.ISODateTimeFormat;
+import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CheckHealthAnswer;
import com.cloud.agent.api.CheckHealthCommand;
@@ -164,7 +137,6 @@
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.network.NetworkModel;
import com.cloud.resource.ServerResourceBase;
import com.cloud.storage.DataStoreRole;
import com.cloud.storage.Storage;
@@ -192,6 +164,23 @@
import com.cloud.utils.script.Script;
import com.cloud.utils.storage.S3.S3Utils;
import com.cloud.vm.SecondaryStorageVm;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.http.HttpContentCompressor;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
public class NfsSecondaryStorageResource extends ServerResourceBase implements
SecondaryStorageResource {
@@ -200,8 +189,6 @@
private static final String TEMPLATE_ROOT_DIR = "template/tmpl";
private static final String VOLUME_ROOT_DIR = "volumes";
private static final String POST_UPLOAD_KEY_LOCATION =
"/etc/cloudstack/agent/ms-psk";
- private static final String cloudStackConfigDriveName = "/cloudstack/";
- private static final String openStackConfigDriveName =
"/openstack/latest/";
private static final Map<String, String> updatableConfigData =
Maps.newHashMap();
static {
@@ -338,32 +325,32 @@ public Answer executeRequest(Command cmd) {
}
private Answer execute(HandleConfigDriveIsoCommand cmd) {
-
if (cmd.isCreate()) {
- s_logger.debug(String.format("VMdata %s, attach = %s",
cmd.getVmData(), cmd.isCreate()));
- if(cmd.getVmData() == null) return new Answer(cmd, false, "No
Vmdata available");
+ if (cmd.getIsoData() == null) {
+ return new Answer(cmd, false, "Invalid config drive ISO data");
+ }
String nfsMountPoint = getRootDir(cmd.getDestStore().getUrl(),
_nfsVersion);
File isoFile = new File(nfsMountPoint, cmd.getIsoFile());
if(isoFile.exists()) {
- if (!cmd.isUpdate()) {
- return new Answer(cmd, true, "ISO already available");
- } else {
- // Find out if we have to recover the password/ssh-key
from the already available ISO.
- try {
- List<String[]> recoveredVmData =
recoverVmData(isoFile);
- for (String[] vmDataEntry : cmd.getVmData()) {
- if
(updatableConfigData.containsKey(vmDataEntry[CONFIGDATA_FILE])
- &&
updatableConfigData.get(vmDataEntry[CONFIGDATA_FILE]).equals(vmDataEntry[CONFIGDATA_DIR]))
{
- updateVmData(recoveredVmData, vmDataEntry);
- }
- }
- cmd.setVmData(recoveredVmData);
- } catch (IOException e) {
- return new Answer(cmd, e);
+ s_logger.debug("config drive iso already exists");
+ }
+ Path tempDir = null;
+ try {
+ tempDir =
java.nio.file.Files.createTempDirectory(ConfigDrive.CONFIGDRIVEDIR);
+ File tmpIsoFile =
ConfigDriveBuilder.base64StringToFile(cmd.getIsoData(),
tempDir.toAbsolutePath().toString(), ConfigDrive.CONFIGDRIVEFILENAME);
+ copyLocalToNfs(tmpIsoFile, new File(cmd.getIsoFile()),
cmd.getDestStore());
+ } catch (IOException | ConfigurationException e) {
+ return new Answer(cmd, false, "Failed due to exception: " +
e.getMessage());
+ } finally {
+ try {
+ if (tempDir != null) {
+ FileUtils.deleteDirectory(tempDir.toFile());
}
+ } catch (IOException ioe) {
+ s_logger.warn("Failed to delete ConfigDrive temporary
directory: " + tempDir.toString(), ioe);
}
}
- return createConfigDriveIsoForVM(cmd);
+ return new Answer(cmd, true, "Successfully saved config drive at
secondary storage");
} else {
DataStoreTO dstore = cmd.getDestStore();
if (dstore instanceof NfsTO) {
@@ -383,237 +370,6 @@ private Answer execute(HandleConfigDriveIsoCommand cmd) {
}
}
- private void updateVmData(List<String[]> recoveredVmData, String[]
vmDataEntry) {
- for (String[] recoveredEntry : recoveredVmData) {
- if
(recoveredEntry[CONFIGDATA_DIR].equals(vmDataEntry[CONFIGDATA_DIR])
- &&
recoveredEntry[CONFIGDATA_FILE].equals(vmDataEntry[CONFIGDATA_FILE])) {
- recoveredEntry[CONFIGDATA_CONTENT] =
vmDataEntry[CONFIGDATA_CONTENT];
- return;
- }
- }
- recoveredVmData.add(vmDataEntry);
- }
-
- private List<String[]> recoverVmData(File isoFile) throws IOException {
- String tempDirName = null;
- List<String[]> recoveredVmData = Lists.newArrayList();
- boolean mounted = false;
- try {
- Path tempDir =
java.nio.file.Files.createTempDirectory("ConfigDrive");
- tempDirName = tempDir.toString();
-
- // Unpack the current config drive file
- Script command = new Script(!_inSystemVM, "mount", _timeout,
s_logger);
- command.add("-o", "loop");
- command.add(isoFile.getAbsolutePath());
- command.add(tempDirName);
- String result = command.execute();
-
- if (result != null) {
- String errMsg = "Unable to mount " + isoFile.getAbsolutePath()
+ " at " + tempDirName + " due to " + result;
- s_logger.error(errMsg);
- throw new IOException(errMsg);
- }
- mounted = true;
-
-
- // Scan directory structure
- for (File configDirectory: (new File(tempDirName,
"cloudstack")).listFiles()){
- for (File configFile: configDirectory.listFiles()) {
- recoveredVmData.add(new String[]{configDirectory.getName(),
-
Files.getNameWithoutExtension(configFile.getName()),
- Files.readFirstLine(configFile,
Charset.defaultCharset())});
- }
- }
-
- } finally {
- if (mounted) {
- Script command = new Script(!_inSystemVM, "umount", _timeout,
s_logger);
- command.add(tempDirName);
- String result = command.execute();
- if (result != null) {
- s_logger.warn("Unable to umount " + tempDirName + " due to
" + result);
- }
- }
- try {
- FileUtils.deleteDirectory(new File(tempDirName));
- } catch (IOException ioe) {
- s_logger.warn("Failed to delete ConfigDrive temporary
directory: " + tempDirName, ioe);
- }
- }
- return recoveredVmData;
- }
-
- public Answer createConfigDriveIsoForVM(HandleConfigDriveIsoCommand cmd) {
- //create folder for the VM
- if (cmd.getVmData() != null) {
-
- Path tempDir = null;
- String tempDirName = null;
- try {
- tempDir =
java.nio.file.Files.createTempDirectory("ConfigDrive");
- tempDirName = tempDir.toString();
-
- //create OpenStack files
- //create folder with empty files
- File openStackFolder = new File(tempDirName +
openStackConfigDriveName);
- if (openStackFolder.exists() || openStackFolder.mkdirs()) {
- File vendorDataFile = new
File(openStackFolder,"vendor_data.json");
- try (FileWriter fw = new FileWriter(vendorDataFile);
BufferedWriter bw = new BufferedWriter(fw)) {
- bw.write("{}");
- } catch (IOException ex) {
- s_logger.error("Failed to create file ", ex);
- return new Answer(cmd, ex);
- }
- File networkDataFile = new File(openStackFolder,
"network_data.json");
- try (FileWriter fw = new FileWriter(networkDataFile);
BufferedWriter bw = new BufferedWriter(fw)) {
- bw.write("{}");
- } catch (IOException ex) {
- s_logger.error("Failed to create file ", ex);
- return new Answer(cmd, ex);
- }
- } else {
- s_logger.error("Failed to create folder " +
openStackFolder);
- return new Answer(cmd, false, "Failed to create folder " +
openStackFolder);
- }
-
- JsonObject metaData = new JsonObject();
- for (String[] item : cmd.getVmData()) {
- String dataType = item[CONFIGDATA_DIR];
- String fileName = item[CONFIGDATA_FILE];
- String content = item[CONFIGDATA_CONTENT];
- s_logger.debug(String.format("[createConfigDriveIsoForVM]
dataType=%s, filename=%s, content=%s",
- dataType, fileName,
(fileName.equals(PASSWORD_FILE)?"********":content)));
-
- // create file with content in folder
- if (dataType != null && !dataType.isEmpty()) {
- //create folder
- File typeFolder = new File(tempDirName +
cloudStackConfigDriveName + dataType);
- if (typeFolder.exists() || typeFolder.mkdirs()) {
- if (StringUtils.isNotEmpty(content)) {
- File file = new File(typeFolder, fileName +
".txt");
- try {
- if (fileName.equals(USERDATA_FILE)) {
- // User Data is passed as a base64
encoded string
- FileUtils.writeByteArrayToFile(file,
Base64.decodeBase64(content));
- } else {
- FileUtils.write(file, content,
com.cloud.utils.StringUtils.getPreferredCharset());
- }
- } catch (IOException ex) {
- s_logger.error("Failed to create file ",
ex);
- return new Answer(cmd, ex);
- }
- }
- } else {
- s_logger.error("Failed to create folder " +
typeFolder);
- return new Answer(cmd, false, "Failed to create
folder " + typeFolder);
- }
-
- //now write the file to the OpenStack directory
- metaData = constructOpenStackMetaData(metaData,
dataType, fileName, content);
- }
- }
-
- File metaDataFile = new File(openStackFolder,
"meta_data.json");
- try (FileWriter fw = new FileWriter(metaDataFile);
BufferedWriter bw = new BufferedWriter(fw)) {
- bw.write(metaData.toString());
- } catch (IOException ex) {
- s_logger.error("Failed to create file ", ex);
- return new Answer(cmd, ex);
- }
-
- String linkResult = linkUserData(tempDirName);
- if (linkResult != null) {
- String errMsg = "Unable to create user_data link due to "
+ linkResult;
- s_logger.warn(errMsg);
- return new Answer(cmd, false, errMsg);
- }
-
- File tmpIsoStore = new File(tempDirName, new
File(cmd.getIsoFile()).getName());
- Script command = new Script(!_inSystemVM,
"/usr/bin/genisoimage", _timeout, s_logger);
- command.add("-o", tmpIsoStore.getAbsolutePath());
- command.add("-ldots");
- command.add("-allow-lowercase");
- command.add("-allow-multidot");
- command.add("-cache-inodes"); // Enable caching inode and
device numbers to find hard links to files.
- command.add("-l");
- command.add("-quiet");
- command.add("-J");
- command.add("-r");
- command.add("-V", cmd.getConfigDriveLabel());
- command.add(tempDirName);
- s_logger.debug("execute command: " + command.toString());
- String result = command.execute();
- if (result != null) {
- String errMsg = "Unable to create iso file: " +
cmd.getIsoFile() + " due to " + result;
- s_logger.warn(errMsg);
- return new Answer(cmd, false, errMsg);
- }
- copyLocalToNfs(tmpIsoStore, new File(cmd.getIsoFile()),
cmd.getDestStore());
-
- } catch (IOException e) {
- return new Answer(cmd, e);
- } catch (ConfigurationException e) {
- s_logger.warn("SecondStorageException ", e);
- return new Answer(cmd, e);
- } finally {
- try {
- FileUtils.deleteDirectory(tempDir.toFile());
- } catch (IOException ioe) {
- s_logger.warn("Failed to delete ConfigDrive temporary
directory: " + tempDirName, ioe);
- }
- }
- }
- return new Answer(cmd);
- }
-
- JsonObject constructOpenStackMetaData(JsonObject metaData, String
dataType, String fileName, String content) {
- if (dataType.equals(NetworkModel.METATDATA_DIR) &&
StringUtils.isNotEmpty(content)) {
- //keys are a special case in OpenStack format
- if (NetworkModel.PUBLIC_KEYS_FILE.equals(fileName)) {
- String[] keyArray = content.replace("\\n", "").split(" ");
- String keyName = "key";
- if (keyArray.length > 3 &&
StringUtils.isNotEmpty(keyArray[2])){
- keyName = keyArray[2];
- }
-
- JsonObject keyLegacy = new JsonObject();
- keyLegacy.addProperty("type", "ssh");
- keyLegacy.addProperty("data", content.replace("\\n", ""));
- keyLegacy.addProperty("name", keyName);
- metaData.add("keys", arrayOf(keyLegacy));
-
- JsonObject key = new JsonObject();
- key.addProperty(keyName, content);
- metaData.add("public_keys", key);
- } else if (NetworkModel.openStackFileMapping.get(fileName) !=
null) {
-
metaData.addProperty(NetworkModel.openStackFileMapping.get(fileName), content);
- }
- }
- return metaData;
- }
-
- private static JsonArray arrayOf(JsonElement... elements) {
- JsonArray array = new JsonArray();
- for (JsonElement element : elements) {
- array.add(element);
- }
- return array;
- }
-
- private String linkUserData(String tempDirName) {
- //Hard link the user_data.txt file with the user_data file in the
OpenStack directory.
- String userDataFilePath = tempDirName + cloudStackConfigDriveName +
"userdata/user_data.txt";
- if ((new File(userDataFilePath).exists())) {
- Script hardLink = new Script(!_inSystemVM, "ln", _timeout,
s_logger);
- hardLink.add(userDataFilePath);
- hardLink.add(tempDirName + openStackConfigDriveName + "user_data");
- s_logger.debug("execute command: " + hardLink.toString());
- return hardLink.execute();
- }
- return null;
- }
-
protected void copyLocalToNfs(File localFile, File isoFile, DataStoreTO
destData) throws ConfigurationException, IOException {
String scriptsDir = "scripts/storage/secondary";
String createVolScr = Script.findScript(scriptsDir, "createvolume.sh");
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
> Config drive - only supported for secondary storage
> ---------------------------------------------------
>
> Key: CLOUDSTACK-10290
> URL: https://issues.apache.org/jira/browse/CLOUDSTACK-10290
> Project: CloudStack
> Issue Type: Bug
> Security Level: Public(Anyone can view this level - this is the
> default.)
> Affects Versions: 4.11.0.0
> Reporter: Rohit Yadav
> Assignee: Daan Hoogland
> Priority: Major
>
> Userdata disk looks like this:
> <disk type='file' device='cdrom'>
> <driver name='qemu' type='raw' cache='none'/>
> <source
> file='/mnt/eba12ff3-c3a6-394a-bf0f-23291f1f6266/configdrive.iso'/>
> <backingStore/>
> <target dev='hdd' bus='ide'/>
> <readonly/>
> <alias name='ide0-1-1'/>
> <address type='drive' controller='0' bus='1' target='0' unit='1'/>
> </disk>
> Mount is:
> root# df /mnt/eba12ff3-c3a6-394a-bf0f-23291f1f6266
> Filesystem 1K-blocks
> Used Available Use% Mounted on
> some-nfs-server.com:/nfs/secondary/ConfigDrive/i-2-24-VM 66391040 2973696
> 63417344 5% /mnt/eba12ff3-c3a6-394a-bf0f-23291f1f6266
>
> issue: where to find a primary storage for a VM that is not yet deployed. The
> configdrive is created before a storage is assigned. This order of execution
> must be reversed for this to work.
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)