This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch 4.11
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.11 by this push:
new acc5fdc CLOUDSTACK-10290: allow config drives on primary storage for
KVM (#2651)
acc5fdc is described below
commit acc5fdcdbd613bedfcd0ca867123e2cd3ba8d5c6
Author: Rohit Yadav <[email protected]>
AuthorDate: Mon May 21 14:27:23 2018 +0530
CLOUDSTACK-10290: allow config drives on primary storage for KVM (#2651)
This introduces a new global setting `vm.configdrive.primarypool.enabled`
to toggle creation/hosting of config drive iso files on primary storage, the
default will be false causing them to be hosted on secondary storage. The
current support is limited from hypervisor resource side and in current
implementation limited to `KVM` only. The next big change is that config drive
is created at a temporary location by management server and shipped to either
KVM or SSVM agent via cmd-answer pa [...]
The APIs to reset ssh public key, password and user-data (via update VM
API) requires that VM should be shutdown. Therefore, in the refactoring I
removed the case of updation of existing ISO. If there are objections I'll
re-put the strategy to detach+attach new config iso as a way of updation. In
the refactored implementation, the folder name is changed to lower-cased
configdrive. And during VM start, migration or shutdown/removal if primary
storage is enable for use, the KVM agent wi [...]
Signed-off-by: Rohit Yadav <[email protected]>
---
.../network/element/UserDataServiceProvider.java | 2 +-
client/pom.xml | 5 +
.../agent/api/HandleConfigDriveIsoCommand.java | 40 +--
.../src/com/cloud/vm/VirtualMachineManager.java | 11 +-
.../com/cloud/vm/VirtualMachineManagerImpl.java | 5 +-
engine/pom.xml | 1 +
engine/storage/configdrive/pom.xml | 43 +++
.../storage/configdrive/ConfigDrive.java | 36 +++
.../storage/configdrive/ConfigDriveBuilder.java | 222 +++++++++++++
.../configdrive/ConfigDriveBuilderTest.java | 65 ++++
packaging/centos7/cloud.spec | 2 +-
plugins/hypervisors/kvm/pom.xml | 5 +
.../kvm/resource/LibvirtComputingResource.java | 5 +-
.../LibvirtHandleConfigDriveCommandWrapper.java | 79 +++++
.../kvm/storage/KVMStoragePoolManager.java | 2 +-
server/pom.xml | 5 +
.../network/element/ConfigDriveNetworkElement.java | 316 ++++++++++---------
server/src/com/cloud/vm/UserVmManagerImpl.java | 48 +--
.../element/ConfigDriveNetworkElementTest.java | 113 ++-----
services/secondary-storage/server/pom.xml | 5 +
.../resource/NfsSecondaryStorageResource.java | 350 ++++-----------------
21 files changed, 758 insertions(+), 602 deletions(-)
diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java
b/api/src/com/cloud/network/element/UserDataServiceProvider.java
index 45ab0d8..bf6d7e8 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.ReservationContext;
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 5653f53..6040275 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -380,6 +380,11 @@
</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>
<version>${project.version}</version>
</dependency>
diff --git a/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
b/core/src/com/cloud/agent/api/HandleConfigDriveIsoCommand.java
index d6d87d4..3d8d8f7 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 class HandleConfigDriveIsoCommand extends Command {
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 class HandleConfigDriveIsoCommand extends Command {
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 556ec86..6041146 100644
--- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java
@@ -49,14 +49,17 @@ import com.cloud.utils.fsm.NoTransitionException;
*/
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 8b3071f..6f2ae85 100755
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1108,10 +1108,11 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
}
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 class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
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 be86f1c..4fc66d1 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 0000000..dc3d118
--- /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 0000000..ec46199
--- /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 0000000..d847aa1
--- /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 0000000..50a4384
--- /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 525421c..96b679c 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 d90e79a..1d0dd66 100644
--- a/plugins/hypervisors/kvm/pom.xml
+++ b/plugins/hypervisors/kvm/pom.xml
@@ -40,6 +40,11 @@
<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>
<version>${cs.rados-java.version}</version>
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 fc5e539..424280c 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 class LibvirtComputingResource extends
ServerResourceBase implements Serv
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 0000000..5e4ef48
--- /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 c613508..4d0523c 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 class KVMStoragePoolManager {
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 534b1a3..55d0a6e 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -142,6 +142,11 @@
<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>
<version>${cs.opensaml.version}</version>
diff --git
a/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
b/server/src/com/cloud/network/element/ConfigDriveNetworkElement.java
index 585b443..dab4a14 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.HashMap;
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.dc.dao.DataCenterDao;
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.NetworkMigrationResponder;
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 @@ public class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
@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 class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
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 class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
}
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 class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
}
@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 class ConfigDriveNetworkElement extends AdapterBase
implements NetworkEle
@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 class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
@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 @@ public class ConfigDriveNetworkElement extends
AdapterBase implements NetworkEle
}
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 ecdd112..4ac48c9 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -653,10 +653,6 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
}
}
-
-
-
-
@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 class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
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 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
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 class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
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 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
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 class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
}
}
-
-
_templateMgr.prepareIsoForVmProfile(profile, dest);
return true;
}
@@ -6135,14 +6144,7 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
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 ff3f40b..e964999 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.Arrays;
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.Mock;
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.VirtualMachineProfileImpl;
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 @@ public class ConfigDriveNetworkElementTest {
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 @@ public class ConfigDriveNetworkElementTest {
@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 class ConfigDriveNetworkElementTest {
_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 class ConfigDriveNetworkElementTest {
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 class ConfigDriveNetworkElementTest {
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 class ConfigDriveNetworkElementTest {
}
@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 class ConfigDriveNetworkElementTest {
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 7c7cf9b..87ac186 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 da3ab32..125512b 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.io.UnsupportedEncodingException;
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 java.util.UUID;
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.TemplateOrVolumePostUploadCommand;
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.TemplateObjectTO;
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.exception.InvalidParameterValueException;
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.OutputInterpreter;
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 @@ public class NfsSecondaryStorageResource extends
ServerResourceBase implements S
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 class NfsSecondaryStorageResource extends
ServerResourceBase implements S
}
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 @@ public class NfsSecondaryStorageResource extends
ServerResourceBase implements S
}
}
- 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");
--
To stop receiving notification emails like this one, please contact
[email protected].