Support for live patching systemVMs and deprecating systemVM.iso. Includes:
- fix systemVM template version
- Include agent.zip, cloud-scripts.tgz to the commons package
- Support for live-patching systemVMs - CPVM, SSVM, Routers
- Fix Unit test
- Remove systemvm.iso dependency
diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java
index 7533e58..16aaba0 100644
--- a/api/src/main/java/com/cloud/event/EventTypes.java
+++ b/api/src/main/java/com/cloud/event/EventTypes.java
@@ -635,6 +635,9 @@
// Storage Policies
public static final String EVENT_IMPORT_VCENTER_STORAGE_POLICIES = "IMPORT.VCENTER.STORAGE.POLICIES";
+ // SystemVM
+ public static final String EVENT_LIVE_PATCH_SYSTEMVM = "LIVE.PATCH.SYSTEM.VM";
+
static {
// TODO: need a way to force author adding event types to declare the entity details as well, with out braking
@@ -1045,6 +1048,7 @@
entityEventDetails.put(EVENT_IMPORT_VCENTER_STORAGE_POLICIES, "StoragePolicies");
entityEventDetails.put(EVENT_IMAGE_STORE_DATA_MIGRATE, ImageStore.class);
+ entityEventDetails.put(EVENT_LIVE_PATCH_SYSTEMVM, "SystemVMs");
}
public static String getEntityForEvent(String eventName) {
diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java
index 56f36a8..9c09833 100644
--- a/api/src/main/java/com/cloud/server/ManagementService.java
+++ b/api/src/main/java/com/cloud/server/ManagementService.java
@@ -39,6 +39,7 @@
import org.apache.cloudstack.api.command.admin.resource.UploadCustomCertificateCmd;
import org.apache.cloudstack.api.command.admin.systemvm.DestroySystemVmCmd;
import org.apache.cloudstack.api.command.admin.systemvm.ListSystemVMsCmd;
+import org.apache.cloudstack.api.command.admin.systemvm.PatchSystemVMCmd;
import org.apache.cloudstack.api.command.admin.systemvm.RebootSystemVmCmd;
import org.apache.cloudstack.api.command.admin.systemvm.ScaleSystemVMCmd;
import org.apache.cloudstack.api.command.admin.systemvm.StopSystemVmCmd;
@@ -424,5 +425,6 @@
void cleanupVMReservations();
+ Pair<Boolean, String> patchSystemVM(PatchSystemVMCmd cmd);
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java
new file mode 100644
index 0000000..481e62c
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/PatchSystemVMCmd.java
@@ -0,0 +1,107 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.admin.systemvm;
+
+import com.cloud.event.EventTypes;
+import com.cloud.user.Account;
+import com.cloud.utils.Pair;
+import com.cloud.vm.VirtualMachine;
+import org.apache.cloudstack.acl.RoleType;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.SystemVmResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.log4j.Logger;
+
+@APICommand(name = "patchSystemVm", description = "Attempts to live patch systemVMs - CPVM, SSVM, Routers ",
+ responseObject = SuccessResponse.class, requestHasSensitiveInfo = false,
+ responseHasSensitiveInfo = false, authorized = { RoleType.Admin })
+public class PatchSystemVMCmd extends BaseAsyncCmd {
+ public static final Logger s_logger = Logger.getLogger(PatchSystemVMCmd.class.getName());
+ private static final String s_name = "patchsystemvmresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+ @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class,
+ description = "patches systemVM - CPVM/SSVM/Router with the specified ID")
+ private Long id;
+
+ @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN,
+ description = "If true, initiates copy of scripts and restart of the agent if if the template version is the latest." +
+ "To be used with ID parameter only")
+ private Boolean force;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+
+ public Long getId() {
+ return id;
+ }
+
+ public boolean isForced() {
+ return force != null ? force : false;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getEventType() {
+ return EventTypes.EVENT_LIVE_PATCH_SYSTEMVM;
+ }
+
+ @Override
+ public String getEventDescription() {
+ return String.format("Attempting to live patch System VM with Id: %s ", this._uuidMgr.getUuid(VirtualMachine.class, getId()));
+ }
+
+ @Override
+ public String getCommandName() {
+ return s_name;
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ Account account = CallContext.current().getCallingAccount();
+ if (account != null) {
+ return account.getId();
+ }
+
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+
+ @Override
+ public void execute() {
+ Pair<Boolean, String> patched = _mgr.patchSystemVM(this);
+ if (patched.first()) {
+ final SuccessResponse response = new SuccessResponse(getCommandName());
+ response.setDisplayText(patched.second());
+ setResponseObject(response);
+ } else {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, patched.second());
+ }
+ }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/PatchSystemVmAnswer.java b/core/src/main/java/com/cloud/agent/api/PatchSystemVmAnswer.java
new file mode 100644
index 0000000..1065768
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/PatchSystemVmAnswer.java
@@ -0,0 +1,44 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.agent.api;
+
+public class PatchSystemVmAnswer extends Answer {
+
+ String templateVersion;
+ String scriptsVersion;
+
+ public PatchSystemVmAnswer() {
+ }
+
+ public PatchSystemVmAnswer(PatchSystemVmCommand cmd, String details, String templateVersion, String scriptsVersion) {
+ super(cmd, true, details);
+ this.templateVersion = templateVersion;
+ this.scriptsVersion = scriptsVersion;
+ }
+
+ public PatchSystemVmAnswer(PatchSystemVmCommand cmd, String details) {
+ super(cmd, false, details);
+ }
+
+ public String getTemplateVersion() {
+ return this.templateVersion;
+ }
+
+ public String getScriptsVersion() {
+ return this.scriptsVersion;
+ }
+}
diff --git a/core/src/main/java/com/cloud/agent/api/PatchSystemVmCommand.java b/core/src/main/java/com/cloud/agent/api/PatchSystemVmCommand.java
new file mode 100644
index 0000000..b9ea2d6
--- /dev/null
+++ b/core/src/main/java/com/cloud/agent/api/PatchSystemVmCommand.java
@@ -0,0 +1,29 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.agent.api;
+
+public class PatchSystemVmCommand extends GetDomRVersionCmd {
+ boolean forced;
+
+ public boolean isForced() {
+ return forced;
+ }
+
+ public void setForced(boolean forced) {
+ this.forced = forced;
+ }
+}
diff --git a/debian/rules b/debian/rules
index ed1559a..1e7f119 100755
--- a/debian/rules
+++ b/debian/rules
@@ -128,7 +128,8 @@
install -D client/target/utilities/bin/cloud-setup-management $(DESTDIR)/usr/bin/cloudstack-setup-management
install -D client/target/utilities/bin/cloud-setup-encryption $(DESTDIR)/usr/bin/cloudstack-setup-encryption
install -D client/target/utilities/bin/cloud-sysvmadm $(DESTDIR)/usr/bin/cloudstack-sysvmadm
- install -D systemvm/dist/systemvm.iso $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/systemvm.iso
+ #install -D systemvm/dist/systemvm.iso $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/systemvm.iso
+ install -D systemvm/dist/* $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/
# We need jasypt for cloud-install-sys-tmplt, so this is a nasty hack to get it into the right place
install -D agent/target/dependencies/jasypt-1.9.3.jar $(DESTDIR)/usr/share/$(PACKAGE)-common/lib
diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml
index 8675352..3b1bba2 100644
--- a/engine/schema/pom.xml
+++ b/engine/schema/pom.xml
@@ -73,10 +73,11 @@
</goals>
<configuration>
<source>
- def projectVersion = project.version
+ def projectVersion = project.properties['project.systemvm.template.version']
+ println(projectVersion)
String[] versionParts = projectVersion.tokenize('.')
- pom.properties['cs.version'] = "4.16"
- pom.properties['patch.version'] = "0"
+ pom.properties['cs.version'] = versionParts[0] + "." + versionParts[1]
+ pom.properties['patch.version'] = versionParts[2]
</source>
</configuration>
</execution>
@@ -146,7 +147,7 @@
<executable>bash</executable>
<arguments>
<argument>templateConfig.sh</argument>
- <armument>${project.version}</armument>
+ <armument>${project.systemvm.template.version}</armument>
</arguments>
</configuration>
</execution>
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
index cf3f728..3c6abbc 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
@@ -367,10 +367,11 @@
return;
}
- SystemVmTemplateRegistration.parseMetadataFile();
- final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue);
- SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(currentVersion.getMajorRelease()) + "." + String.valueOf(currentVersion.getMinorRelease());
- SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(currentVersion.getPatchRelease());
+ String csVersion = SystemVmTemplateRegistration.parseMetadataFile();
+ final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion);
+ final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue);
+ SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(sysVmVersion.getMajorRelease()) + "." + String.valueOf(sysVmVersion.getMinorRelease());
+ SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease());
s_logger.info("DB version = " + dbVersion + " Code Version = " + currentVersion);
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java
index d788ada..4119b11 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java
@@ -36,6 +36,7 @@
import com.cloud.upgrade.dao.BasicTemplateDataStoreDaoImpl;
import com.cloud.user.Account;
import com.cloud.utils.DateUtil;
+import com.cloud.utils.EncryptionUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.UriUtils;
import com.cloud.utils.db.GlobalLock;
@@ -54,7 +55,6 @@
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
-import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.ini4j.Ini;
@@ -64,7 +64,6 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -351,16 +350,6 @@
}
}
- private String calculateChecksum(File file) {
- try (InputStream is = Files.newInputStream(Paths.get(file.getPath()))) {
- return DigestUtils.md5Hex(is);
- } catch (IOException e) {
- String errMsg = "Failed to calculate template checksum";
- LOGGER.error(errMsg, e);
- throw new CloudRuntimeException(errMsg, e);
- }
- }
-
public Long getRegisteredTemplateId(Pair<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second());
Long templateId = null;
@@ -690,7 +679,7 @@
}
}
- public static void parseMetadataFile() {
+ public static String parseMetadataFile() {
try {
Ini ini = new Ini();
ini.load(new FileReader(METADATA_FILE));
@@ -702,6 +691,8 @@
NewTemplateChecksum.put(hypervisorType, section.get("checksum"));
NewTemplateUrl.put(hypervisorType, section.get("downloadurl"));
}
+ Ini.Section section = ini.get("default");
+ return section.get("version");
} catch (Exception e) {
String errMsg = String.format("Failed to parse systemVM template metadata file: %s", METADATA_FILE);
LOGGER.error(errMsg, e);
@@ -735,7 +726,7 @@
}
File tempFile = new File(TEMPLATES_PATH + matchedTemplate);
- String templateChecksum = calculateChecksum(tempFile);
+ String templateChecksum = EncryptionUtil.calculateChecksum(tempFile);
if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) {
LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor))));
templatesFound = false;
@@ -812,9 +803,6 @@
private void updateRegisteredTemplateDetails(Long templateId, Map.Entry<Hypervisor.HypervisorType, String> hypervisorAndTemplateName) {
VMTemplateVO templateVO = vmTemplateDao.findById(templateId);
templateVO.setTemplateType(Storage.TemplateType.SYSTEM);
- if (Hypervisor.HypervisorType.VMware == templateVO.getHypervisorType()) {
- templateVO.setDeployAsIs(true);
- }
boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO);
if (!updated) {
String errMsg = String.format("updateSystemVmTemplates:Exception while updating template with id %s to be marked as 'system'", templateId);
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql
index 24c5b79..2fd0e5e 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41600to41610.sql
@@ -17,4 +17,6 @@
--;
-- Schema upgrade from 4.16.0.0 to 4.16.1.0
---;
\ No newline at end of file
+--;
+
+UPDATE `cloud`.`vm_template` set deploy_as_is = 0 where id = 8;
\ No newline at end of file
diff --git a/engine/schema/templateConfig.sh b/engine/schema/templateConfig.sh
index c309353..891c73d 100644
--- a/engine/schema/templateConfig.sh
+++ b/engine/schema/templateConfig.sh
@@ -23,8 +23,10 @@
subversion1="$(cut -d'.' -f1 <<<"$version")"
subversion2="$(cut -d'.' -f2 <<<"$version")"
minorversion="$(cut -d'.' -f3 <<<"$version")"
+ securityversion="$(cut -d'.' -f4 <<<"$version")"
export CS_VERSION="${subversion1}"."${subversion2}"
export CS_MINOR_VERSION="${minorversion}"
+ export VERSION="${CS_VERSION}.${CS_MINOR_VERSION}"
}
function getGenericName() {
@@ -52,12 +54,14 @@
function createMetadataFile() {
local fileData=$(cat $SOURCEFILE)
+ echo -e "["default"]\nversion = $VERSION.${securityversion}\n" >> $METADATAFILE
for i in "${!templates[@]}"
do
section="$i"
hvName=$(getGenericName $i)
- templatename="systemvm-${i}-${CS_VERSION}.${CS_MINOR_VERSION}"
- checksum=$(getChecksum "$fileData" $hvName)
+
+ templatename="systemvm-${i}-${VERSION}"
+ checksum=$(getChecksum "$fileData" "$VERSION-$hvName")
downloadurl="${templates[$i]}"
filename=$(echo ${downloadurl##*'/'})
echo -e "["$section"]\ntemplatename = $templatename\nchecksum = $checksum\ndownloadurl = $downloadurl\nfilename = $filename\n" >> $METADATAFILE
@@ -66,12 +70,12 @@
declare -A templates
getTemplateVersion $1
-templates=( ["kvm"]="https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-${CS_VERSION}.${CS_MINOR_VERSION}-kvm.qcow2.bz2"
- ["vmware"]="https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-${CS_VERSION}.${CS_MINOR_VERSION}-vmware.ova"
- ["xenserver"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$CS_VERSION.$CS_MINOR_VERSION-xen.vhd.bz2"
- ["hyperv"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$CS_VERSION.$CS_MINOR_VERSION-hyperv.vhd.zip"
- ["lxc"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$CS_VERSION.$CS_MINOR_VERSION-kvm.qcow2.bz2"
- ["ovm3"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$CS_VERSION.$CS_MINOR_VERSION-ovm.raw.bz2" )
+templates=( ["kvm"]="https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-kvm.qcow2.bz2"
+ ["vmware"]="https://download.cloudstack.org/systemvm/${CS_VERSION}/systemvmtemplate-$VERSION-vmware.ova"
+ ["xenserver"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-xen.vhd.bz2"
+ ["hyperv"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-hyperv.vhd.zip"
+ ["lxc"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-kvm.qcow2.bz2"
+ ["ovm3"]="https://download.cloudstack.org/systemvm/$CS_VERSION/systemvmtemplate-$VERSION-ovm.raw.bz2" )
PARENTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/dist/systemvm-templates/"
diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec
index 0728f58..a932c94 100644
--- a/packaging/centos7/cloud.spec
+++ b/packaging/centos7/cloud.spec
@@ -231,7 +231,8 @@
mkdir -p ${RPM_BUILD_ROOT}%{python_sitearch}/
mkdir -p ${RPM_BUILD_ROOT}/usr/bin
cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
-install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+#install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+install -D systemvm/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/
install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}%{python_sitearch}/
python3 -m py_compile ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
@@ -600,7 +601,11 @@
%dir %attr(0755,root,root) %{_datadir}/%{name}-common/vms
%attr(0755,root,root) %{_datadir}/%{name}-common/scripts
%attr(0755,root,root) /usr/bin/cloudstack-sccs
+# TODO: Remove systemvm.iso
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/systemvm.iso
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/agent.zip
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/cloud-scripts.tgz
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/patch-sysvms.sh
%attr(0644,root,root) %{python_sitearch}/cloud_utils.py
%attr(0644,root,root) %{python_sitearch}/__pycache__/*
%attr(0644,root,root) %{python_sitearch}/cloudutils/*
diff --git a/packaging/centos8/cloud.spec b/packaging/centos8/cloud.spec
index 31d85dd..d19e4fd 100644
--- a/packaging/centos8/cloud.spec
+++ b/packaging/centos8/cloud.spec
@@ -224,7 +224,8 @@
mkdir -p ${RPM_BUILD_ROOT}%{python_sitearch}/
mkdir -p ${RPM_BUILD_ROOT}/usr/bin
cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
-install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+#install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+install -D systemvm/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/
install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}%{python_sitearch}/
python3 -m py_compile ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
@@ -588,7 +589,11 @@
%dir %attr(0755,root,root) %{_datadir}/%{name}-common/vms
%attr(0755,root,root) %{_datadir}/%{name}-common/scripts
%attr(0755,root,root) /usr/bin/cloudstack-sccs
+# TODO: Remove systemvm.iso
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/systemvm.iso
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/agent.zip
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/cloud-scripts.tgz
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/patch-sysvms.sh
%attr(0644,root,root) %{python_sitearch}/cloud_utils.py
%attr(0644,root,root) %{python_sitearch}/__pycache__/*
%attr(0644,root,root) %{python_sitearch}/cloudutils/*
diff --git a/packaging/suse15/cloud.spec b/packaging/suse15/cloud.spec
index 30300c6..cc75a96 100644
--- a/packaging/suse15/cloud.spec
+++ b/packaging/suse15/cloud.spec
@@ -226,7 +226,8 @@
mkdir -p ${RPM_BUILD_ROOT}%{python_sitearch}/
mkdir -p ${RPM_BUILD_ROOT}/usr/bin
cp -r scripts/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/scripts
-install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+#install -D systemvm/dist/systemvm.iso ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/systemvm.iso
+install -D systemvm/dist/* ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/vms/
install python/lib/cloud_utils.py ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
cp -r python/lib/cloudutils ${RPM_BUILD_ROOT}%{python_sitearch}/
python3 -m py_compile ${RPM_BUILD_ROOT}%{python_sitearch}/cloud_utils.py
@@ -582,7 +583,11 @@
%dir %attr(0755,root,root) %{_datadir}/%{name}-common/vms
%attr(0755,root,root) %{_datadir}/%{name}-common/scripts
%attr(0755,root,root) /usr/bin/cloudstack-sccs
+# TODO: Remove systemvm.iso
%attr(0644, root, root) %{_datadir}/%{name}-common/vms/systemvm.iso
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/agent.zip
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/cloud-scripts.tgz
+%attr(0644, root, root) %{_datadir}/%{name}-common/vms/patch-sysvms.sh
%attr(0644,root,root) %{python_sitearch}/cloud_utils.py
%attr(0644,root,root) %{python_sitearch}/__pycache__/*
%attr(0644,root,root) %{python_sitearch}/cloudutils/*
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 9684b7e..4a3f778 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -280,6 +280,10 @@
private static final String AARCH64 = "aarch64";
public static final String RESIZE_NOTIFY_ONLY = "NOTIFYONLY";
+ public static final String BASEPATH = "/usr/share/cloudstack-common/vms/";
+
+ public static String[] srcFiles = new String[] { "agent.zip", "cloud-scripts.tgz" };
+ public static String[] newSrcFiles = new String[] { "agent.zip", "cloud-scripts.tgz", "patch-sysvms.sh" };
private String _modifyVlanPath;
private String _versionstringpath;
@@ -403,7 +407,7 @@
s_powerStatesTable.put(DomainState.VIR_DOMAIN_SHUTDOWN, PowerState.PowerOff);
}
- private VirtualRoutingResource _virtRouterResource;
+ public VirtualRoutingResource _virtRouterResource;
private String _pingTestPath;
@@ -463,7 +467,7 @@
try {
SshHelper.scpTo(routerIp, 3922, "root", permKey, null, path, content.getBytes(), filename, null);
} catch (final Exception e) {
- s_logger.warn("Fail to create file " + path + filename + " in VR " + routerIp, e);
+ s_logger.warn("Failed to create file " + path + filename + " in VR " + routerIp, e);
details = e.getMessage();
success = false;
}
@@ -2906,7 +2910,7 @@
if (vmSpec.getType() != VirtualMachine.Type.User) {
if (_sysvmISOPath != null) {
final DiskDef iso = new DiskDef();
- iso.defISODisk(_sysvmISOPath);
+ // iso.defISODisk(_sysvmISOPath);
if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
iso.setBusType(DiskDef.DiskBus.SCSI);
}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPatchSystemVmCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPatchSystemVmCommandWrapper.java
new file mode 100644
index 0000000..392ccb6
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtPatchSystemVmCommandWrapper.java
@@ -0,0 +1,132 @@
+// 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 com.cloud.agent.api.Answer;
+import com.cloud.agent.api.PatchSystemVmAnswer;
+import com.cloud.agent.api.PatchSystemVmCommand;
+import com.cloud.agent.api.routing.NetworkElementCommand;
+import com.cloud.agent.resource.virtualnetwork.VRScripts;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.utils.EncryptionUtil;
+import com.cloud.utils.ExecutionResult;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+import com.cloud.utils.ssh.SshHelper;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@ResourceWrapper(handles = PatchSystemVmCommand.class)
+public class LibvirtPatchSystemVmCommandWrapper extends CommandWrapper<PatchSystemVmCommand, Answer, LibvirtComputingResource> {
+ private static final Logger s_logger = Logger.getLogger(LibvirtPatchSystemVmCommandWrapper.class);
+ private static int sshPort = Integer.parseInt(LibvirtComputingResource.DEFAULTDOMRSSHPORT);
+ private static File pemFile = new File(LibvirtComputingResource.SSHPRVKEYPATH);
+
+ @Override
+ public Answer execute(PatchSystemVmCommand cmd, LibvirtComputingResource serverResource) {
+ final String controlIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
+ final String sysVMName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
+ ExecutionResult result;
+ try {
+ result = getSystemVmVersionAndChecksum(serverResource, controlIp);
+ scpPatchFiles(controlIp);
+ } catch (CloudRuntimeException e) {
+ return new PatchSystemVmAnswer(cmd, e.getMessage());
+ }
+
+ final String[] lines = result.getDetails().split("&");
+ // TODO: do we fail, or patch anyway??
+ if (lines.length != 2) {
+ return new PatchSystemVmAnswer(cmd, result.getDetails());
+ }
+
+ String scriptChecksum = lines[1].trim();
+ String checksum = calculateCurrentChecksum(sysVMName).trim();
+
+ if (!StringUtils.isEmpty(checksum) && checksum.equals(scriptChecksum)) {
+ if (!cmd.isForced()) {
+ String msg = String.format("No change in the scripts checksum, not patching systemVM %s", sysVMName);
+ s_logger.info(msg);
+ return new PatchSystemVmAnswer(cmd, msg, lines[0], lines[1]);
+ }
+ }
+
+ Pair<Boolean, String> patchResult = null;
+ try {
+ patchResult = SshHelper.sshExecute(controlIp, sshPort, "root",
+ pemFile, null, "/home/cloud/patch-sysvms.sh", 10000, 10000, 60000);
+ } catch (Exception e) {
+ return new PatchSystemVmAnswer(cmd, e.getMessage());
+ }
+
+ if (patchResult.first()) {
+ return new PatchSystemVmAnswer(cmd, String.format("Successfully patched systemVM %s ", sysVMName), lines[0], lines[1]);
+ }
+ return new PatchSystemVmAnswer(cmd, patchResult.second());
+ }
+
+ private String calculateCurrentChecksum(String name) {
+ String cloudScriptsPath = Script.findScript("", "vms/cloud-scripts.tgz");
+ if (cloudScriptsPath == null) {
+ throw new CloudRuntimeException(String.format("Unable to find cloudScripts path, cannot update SystemVM %s", name));
+ }
+ String md5sum = EncryptionUtil.calculateChecksum(new File(cloudScriptsPath));
+ return md5sum;
+ }
+
+ private ExecutionResult getSystemVmVersionAndChecksum(LibvirtComputingResource serverResource, String controlIp) {
+ ExecutionResult result;
+ try {
+ result = serverResource.executeInVR(controlIp, VRScripts.VERSION, null);
+ if (!result.isSuccess()) {
+ String errMsg = String.format("GetSystemVMVersionCmd on %s failed, message %s", controlIp, result.getDetails());
+ s_logger.error(errMsg);
+ throw new CloudRuntimeException(errMsg);
+ }
+ } catch (final Exception e) {
+ final String msg = "GetSystemVMVersionCmd failed due to " + e;
+ s_logger.error(msg, e);
+ throw new CloudRuntimeException(msg, e);
+ }
+ return result;
+ }
+
+ private void scpPatchFiles(String controlIp) {
+ try {
+ List<String> srcFiles = Arrays.asList(LibvirtComputingResource.newSrcFiles);
+ srcFiles = srcFiles.stream()
+ .map(file -> LibvirtComputingResource.BASEPATH + file) // Using Lambda notation to update the entries
+ .collect(Collectors.toList());
+ String[] newSrcFiles = srcFiles.toArray(new String[0]);
+ SshHelper.scpTo(controlIp, sshPort, "root", pemFile, null,
+ "/home/cloud/", newSrcFiles, "0755");
+ } catch (Exception e) {
+ String errMsg = "Failed to scp files to system VM";
+ s_logger.error(errMsg, e);
+ throw new CloudRuntimeException(errMsg, e);
+ }
+ }
+}
+
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
index f151255..b95c163 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartCommandWrapper.java
@@ -19,8 +19,13 @@
package com.cloud.hypervisor.kvm.resource.wrapper;
+import java.io.File;
import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import com.cloud.utils.ssh.SshHelper;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.DomainInfo.DomainState;
@@ -115,6 +120,22 @@
break;
}
}
+
+ try {
+ List<String> srcFiles = Arrays.asList(LibvirtComputingResource.srcFiles);
+ srcFiles = srcFiles.stream()
+ .map(file -> LibvirtComputingResource.BASEPATH + file)
+ .collect(Collectors.toList());
+ File pemFile = new File(LibvirtComputingResource.SSHPRVKEYPATH);
+ SshHelper.scpTo(controlIp, 3922, "root", pemFile, null,
+ "/home/cloud/", srcFiles.toArray(new String[0]), "0755");
+ // TODO: May want to remove this when cert patching logic is moved
+ Thread.sleep(10000);
+ } catch (Exception e) {
+ String errMsg = "Failed to scp files to system VM. Patching of systemVM failed";
+ s_logger.error(errMsg, e);
+ return new StartAnswer(command, String.format("%s due to: %s", errMsg, e.getMessage()));
+ }
}
}
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index c744299..c1bb5bb 100644
--- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -54,6 +54,7 @@
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
+import com.cloud.utils.ssh.SshHelper;
import org.apache.cloudstack.storage.command.AttachAnswer;
import org.apache.cloudstack.storage.command.AttachCommand;
import org.apache.cloudstack.utils.linux.CPUStat;
@@ -210,7 +211,7 @@
import org.libvirt.VcpuInfo;
@RunWith(PowerMockRunner.class)
-@PrepareForTest(value = {MemStat.class})
+@PrepareForTest(value = {MemStat.class, SshHelper.class})
@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*"})
public class LibvirtComputingResourceTest {
@@ -5278,7 +5279,9 @@
}
@Test
- public void testStartCommand() {
+ public void testStartCommand() throws Exception {
+ PowerMockito.mockStatic(SshHelper.class);
+ PowerMockito.doNothing().when(SshHelper.class, "scpTo", Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), nullable(String.class), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString());
final VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
final com.cloud.host.Host host = Mockito.mock(com.cloud.host.Host.class);
final boolean executeInSequence = false;
@@ -5352,7 +5355,9 @@
}
@Test
- public void testStartCommandIsolationEc2() {
+ public void testStartCommandIsolationEc2() throws Exception {
+ PowerMockito.mockStatic(SshHelper.class);
+ PowerMockito.doNothing().when(SshHelper.class, "scpTo", Mockito.anyString(), Mockito.anyInt(), Mockito.anyString(), Mockito.any(File.class), nullable(String.class), Mockito.anyString(), Mockito.any(String[].class), Mockito.anyString());
final VirtualMachineTO vmSpec = Mockito.mock(VirtualMachineTO.class);
final com.cloud.host.Host host = Mockito.mock(com.cloud.host.Host.class);
final boolean executeInSequence = false;
diff --git a/pom.xml b/pom.xml
index 67cbc6e..69f520b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
<!-- keep in alphabetic order -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <project.systemvm.template.version>4.16.0.0</project.systemvm.template.version>
<!-- Build properties -->
<cs.jdk.version>11</cs.jdk.version>
diff --git a/scripts/vm/hypervisor/xenserver/xcpserver/patch b/scripts/vm/hypervisor/xenserver/xcpserver/patch
index 862aa2e..32c7c46 100644
--- a/scripts/vm/hypervisor/xenserver/xcpserver/patch
+++ b/scripts/vm/hypervisor/xenserver/xcpserver/patch
@@ -32,6 +32,8 @@
ovstunnel=..,0755,/etc/xapi.d/plugins
vmopsSnapshot=..,0755,/etc/xapi.d/plugins
systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso
+agent.zip=../../../../../vms,0644,/opt/xensource/packages/iso
+cloud-scripts.tgz=../../../../../vms,0644,/opt/xensource/packages/iso
id_rsa.cloud=../../../systemvm,0600,/root/.ssh
network_info.sh=..,0755,/opt/cloud/bin
setupxenserver.sh=..,0755,/opt/cloud/bin
diff --git a/server/src/main/java/com/cloud/api/ResponseObjectTypeAdapter.java b/server/src/main/java/com/cloud/api/ResponseObjectTypeAdapter.java
index 44baedc..f6f777e 100644
--- a/server/src/main/java/com/cloud/api/ResponseObjectTypeAdapter.java
+++ b/server/src/main/java/com/cloud/api/ResponseObjectTypeAdapter.java
@@ -22,6 +22,7 @@
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.response.ExceptionResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.google.gson.JsonElement;
@@ -38,6 +39,9 @@
if (responseObj instanceof SuccessResponse) {
obj.addProperty("success", ((SuccessResponse)responseObj).getSuccess());
+ if (!StringUtils.isEmpty(((SuccessResponse) responseObj).getDisplayText())) {
+ obj.addProperty("details", ((SuccessResponse)responseObj).getDisplayText());
+ }
return obj;
} else if (responseObj instanceof ExceptionResponse) {
obj.addProperty("errorcode", ((ExceptionResponse)responseObj).getErrorCode());
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 08ae0ad..78dd3fb 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -44,8 +44,15 @@
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import com.cloud.agent.api.PatchSystemVmAnswer;
+import com.cloud.agent.api.PatchSystemVmCommand;
+import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.dc.DomainVlanMapVO;
import com.cloud.dc.dao.DomainVlanMapDao;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.network.Networks;
+import com.cloud.vm.NicVO;
+import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
@@ -224,6 +231,7 @@
import org.apache.cloudstack.api.command.admin.systemvm.DestroySystemVmCmd;
import org.apache.cloudstack.api.command.admin.systemvm.ListSystemVMsCmd;
import org.apache.cloudstack.api.command.admin.systemvm.MigrateSystemVMCmd;
+import org.apache.cloudstack.api.command.admin.systemvm.PatchSystemVMCmd;
import org.apache.cloudstack.api.command.admin.systemvm.RebootSystemVmCmd;
import org.apache.cloudstack.api.command.admin.systemvm.ScaleSystemVMCmd;
import org.apache.cloudstack.api.command.admin.systemvm.StartSystemVMCmd;
@@ -761,6 +769,7 @@
static final ConfigKey<Integer> sshKeyLength = new ConfigKey<Integer>("Advanced", Integer.class, "ssh.key.length", "2048", "Specifies custom SSH key length (bit)", true, ConfigKey.Scope.Global);
static final ConfigKey<Boolean> humanReadableSizes = new ConfigKey<Boolean>("Advanced", Boolean.class, "display.human.readable.sizes", "true", "Enables outputting human readable byte sizes to logs and usage records.", false, ConfigKey.Scope.Global);
public static final ConfigKey<String> customCsIdentifier = new ConfigKey<String>("Advanced", String.class, "custom.cs.identifier", UUID.randomUUID().toString().split("-")[0].substring(4), "Custom identifier for the cloudstack installation", true, ConfigKey.Scope.Global);
+ private static final VirtualMachine.Type []systemVmTypes = { VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.DomainRouter };
@Inject
public AccountManager _accountMgr;
@@ -823,7 +832,7 @@
@Inject
private StoragePoolJoinDao _poolJoinDao;
@Inject
- private NetworkDao _networkDao;
+ private NetworkDao networkDao;
@Inject
private StorageManager _storageMgr;
@Inject
@@ -896,6 +905,8 @@
private AnnotationDao annotationDao;
@Inject
private DomainVlanMapDao _domainVlanMapDao;
+ @Inject
+ private NicDao nicDao;
private LockControllerListener _lockControllerListener;
private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker"));
@@ -2133,9 +2144,9 @@
if (ip == null) {
throw new InvalidParameterValueException("Please specify a valid ipaddress id");
}
- network = _networkDao.findById(ip.getSourceNetworkId());
+ network = networkDao.findById(ip.getSourceNetworkId());
} else {
- network = _networkDao.findById(networkId);
+ network = networkDao.findById(networkId);
}
if (network == null || network.getGuestType() != Network.GuestType.Shared) {
throw new InvalidParameterValueException("Please specify a valid network id");
@@ -2197,7 +2208,7 @@
}
if (associatedNetworkId != null) {
- _accountMgr.checkAccess(caller, null, false, _networkDao.findById(associatedNetworkId));
+ _accountMgr.checkAccess(caller, null, false, networkDao.findById(associatedNetworkId));
sc.setParameters("associatedNetworkIdEq", associatedNetworkId);
}
if (vpcId != null) {
@@ -2213,7 +2224,7 @@
Long zoneId = zone;
Account owner = _accountMgr.finalizeOwner(CallContext.current().getCallingAccount(), cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
if (associatedNetworkId != null) {
- NetworkVO guestNetwork = _networkDao.findById(associatedNetworkId);
+ NetworkVO guestNetwork = networkDao.findById(associatedNetworkId);
if (zoneId == null) {
zoneId = guestNetwork.getDataCenterId();
} else if (zoneId != guestNetwork.getDataCenterId()) {
@@ -3486,6 +3497,7 @@
cmdList.add(UploadResourceIconCmd.class);
cmdList.add(DeleteResourceIconCmd.class);
cmdList.add(ListResourceIconCmd.class);
+ cmdList.add(PatchSystemVMCmd.class);
// Out-of-band management APIs for admins
cmdList.add(EnableOutOfBandManagementForHostCmd.class);
@@ -3891,7 +3903,7 @@
boolean elasticLoadBalancerEnabled = false;
boolean KVMSnapshotEnabled = false;
String supportELB = "false";
- final List<NetworkVO> networks = _networkDao.listSecurityGroupEnabledNetworks();
+ final List<NetworkVO> networks = networkDao.listSecurityGroupEnabledNetworks();
if (networks != null && !networks.isEmpty()) {
securityGroupsEnabled = true;
final String elbEnabled = _configDao.getValue(Config.ElasticLoadBalancerEnabled.key());
@@ -4592,6 +4604,72 @@
_dpMgr.cleanupVMReservations();
}
+ @Override
+ public Pair<Boolean, String> patchSystemVM(PatchSystemVMCmd cmd) {
+ Long systemVmId = cmd.getId();
+ boolean forced = cmd.isForced();
+
+ if (systemVmId == null) {
+ throw new InvalidParameterValueException("Please provide a valid ID of a system VM to be patched");
+ }
+
+ final VMInstanceVO systemVm = _vmInstanceDao.findByIdTypes(systemVmId, systemVmTypes);
+ if (systemVm == null) {
+ throw new InvalidParameterValueException("Unable to find SystemVm with id " + systemVmId);
+ }
+
+ return updateSystemVM(systemVm, forced);
+ }
+
+
+ private String getControlIp(final long systemVmId) {
+ String controlIpAddress = null;
+ final List<NicVO> nics = nicDao.listByVmId(systemVmId);
+ for (final NicVO n : nics) {
+ final NetworkVO nc = networkDao.findById(n.getNetworkId());
+ if (nc != null && nc.getTrafficType() == Networks.TrafficType.Control) {
+ controlIpAddress = n.getIPv4Address();
+ // router will have only one control IP
+ break;
+ }
+ }
+
+ if (controlIpAddress == null) {
+ s_logger.warn("Unable to find systemVm's control ip in its attached NICs!. systemVmId: " + systemVmId);
+ VMInstanceVO systemVM = _vmInstanceDao.findById(systemVmId);
+ return systemVM.getPrivateIpAddress();
+ }
+
+ return controlIpAddress;
+ }
+
+ private Pair<Boolean, String> updateSystemVM(VMInstanceVO systemVM, boolean forced) {
+ return patchSystemVm(systemVM, forced);
+ }
+
+ private Pair<Boolean, String> patchSystemVm(VMInstanceVO systemVM, boolean forced) {
+ PatchSystemVmAnswer answer = new PatchSystemVmAnswer();
+ final PatchSystemVmCommand command = new PatchSystemVmCommand();
+ command.setAccessDetail(NetworkElementCommand.ROUTER_IP, getControlIp(systemVM.getId()));
+ command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, systemVM.getInstanceName());
+ command.setForced(forced);
+ try {
+ answer = (PatchSystemVmAnswer) _agentMgr.send(systemVM.getHostId(), command);
+ if (!answer.getResult()) {
+ String errMsg = String.format("Failed to patch systemVM %s due to %s", systemVM.getInstanceName(), answer.getDetails());
+ s_logger.error(errMsg);
+ return new Pair<>(false, errMsg);
+ }
+
+ } catch (AgentUnavailableException | OperationTimedoutException e) {
+ String errMsg = "SystemVM live patch failed";
+ s_logger.error(errMsg, e);
+ return new Pair<>(false, String.format("%s due to: %s", errMsg, e.getMessage()));
+ }
+ s_logger.info(String.format("Successfully patch system VM %s", systemVM.getInstanceName()));
+ return new Pair<>(true, answer.getDetails());
+ }
+
public List<StoragePoolAllocator> getStoragePoolAllocators() {
return _storagePoolAllocators;
}
diff --git a/systemvm/debian/etc/systemd/system/cloud-early-config.service b/systemvm/debian/etc/systemd/system/cloud-early-config.service
index 2af5276..cfaf5e7 100644
--- a/systemvm/debian/etc/systemd/system/cloud-early-config.service
+++ b/systemvm/debian/etc/systemd/system/cloud-early-config.service
@@ -2,11 +2,8 @@
Description=CloudStack post-boot patching service using cmdline
DefaultDependencies=no
-Before=network-pre.target
-Wants=network-pre.target
-
-Requires=local-fs.target
-After=local-fs.target
+Requires=local-fs.target cloud-preinit.service
+After=local-fs.target cloud-preinit.service
[Install]
WantedBy=multi-user.target
diff --git a/systemvm/debian/etc/systemd/system/cloud-postinit.service b/systemvm/debian/etc/systemd/system/cloud-postinit.service
index cb20aaf..f5b23e2 100644
--- a/systemvm/debian/etc/systemd/system/cloud-postinit.service
+++ b/systemvm/debian/etc/systemd/system/cloud-postinit.service
@@ -1,7 +1,7 @@
[Unit]
Description=CloudStack post-patching init script
After=cloud-early-config.service network.target local-fs.target
-Before=ssh.service
+#Before=ssh.service
[Install]
WantedBy=multi-user.target
diff --git a/systemvm/debian/etc/systemd/system/cloud-preinit.service b/systemvm/debian/etc/systemd/system/cloud-preinit.service
new file mode 100644
index 0000000..373cd9e
--- /dev/null
+++ b/systemvm/debian/etc/systemd/system/cloud-preinit.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=CloudStack service to initialize interfaces
+DefaultDependencies=no
+
+Before=network-pre.target
+Wants=network-pre.target
+
+Requires=local-fs.target
+After=local-fs.target
+
+[Install]
+WantedBy=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/opt/cloud/bin/setup/init.sh
+RemainAfterExit=true
+TimeoutStartSec=5min
diff --git a/systemvm/debian/opt/cloud/bin/setup/bootstrap.sh b/systemvm/debian/opt/cloud/bin/setup/bootstrap.sh
index 2335d64..3f64be7 100755
--- a/systemvm/debian/opt/cloud/bin/setup/bootstrap.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/bootstrap.sh
@@ -15,7 +15,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
+set -x
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
CMDLINE=/var/cache/cloud/cmdline
@@ -29,124 +29,6 @@
log_action_msg "$@"
}
-hypervisor() {
- if [ -d /proc/xen ]; then
- mount -t xenfs none /proc/xen
- $(dmesg | grep -q "Xen HVM")
- if [ $? -eq 0 ]; then # 1=PV,0=HVM
- echo "xen-hvm" && return 0
- else
- echo "xen-pv" && return 0
- fi
- fi
-
- [ -x /usr/sbin/virt-what ] && local facts=( $(virt-what) )
- if [ "$facts" != "" ]; then
- # Xen HVM is recognized as Hyperv when Viridian extensions are enabled
- if [ "${facts[-1]}" == "xen-domU" ] && [ "${facts[0]}" == "hyperv" ]; then
- echo "xen-hvm" && return 0
- else
- echo ${facts[-1]} && return 0
- fi
- fi
-
- grep -q QEMU /proc/cpuinfo && echo "kvm" && return 0
- grep -q QEMU /var/log/messages && echo "kvm" && return 0
-
- vmware-checkvm &> /dev/null && echo "vmware" && return 0
-
- echo "unknown" && return 1
-}
-
-config_guest() {
- [ ! -d /proc/xen ] && sed -i 's/^vc/#vc/' /etc/inittab && telinit q
- [ -d /proc/xen ] && sed -i 's/^#vc/vc/' /etc/inittab && telinit q
-
- systemctl daemon-reload
-
- case $HYPERVISOR in
- xen-pv|xen-domU)
- systemctl stop ntpd
- systemctl disable ntpd
- systemctl enable xe-daemon
- systemctl start xe-daemon
-
- cat /proc/cmdline > $CMDLINE
- sed -i "s/%/ /g" $CMDLINE
- ;;
- xen-hvm)
- systemctl stop ntpd
- systemctl disable ntpd
- systemctl enable xe-daemon
- systemctl start xe-daemon
-
- if [ ! -f /usr/bin/xenstore-read ]; then
- log_it "ERROR: xentools not installed, cannot found xenstore-read" && exit 5
- fi
- /usr/bin/xenstore-read vm-data/cloudstack/init > $CMDLINE
- sed -i "s/%/ /g" $CMDLINE
- ;;
- kvm)
- # Configure kvm hotplug support
- if grep -E 'CONFIG_HOTPLUG_PCI=y|CONFIG_HOTPLUG_PCI_ACPI=y' /boot/config-`uname -r`; then
- log_it "acpiphp and pci_hotplug module already compiled in"
- else
- modprobe acpiphp 2> /dev/null && log_it "acpiphp module loaded" || true
- modprobe pci_hotplug 2> /dev/null && log_it "pci_hotplug module loaded" || true
- fi
-
- sed -i -e "/^s0:2345:respawn.*/d" /etc/inittab
- sed -i -e "/6:23:respawn/a\s0:2345:respawn:/sbin/getty -L 115200 ttyS0 vt102" /etc/inittab
- systemctl enable qemu-guest-agent
- systemctl start qemu-guest-agent
-
- # Wait for $CMDLINE file to be written by the qemu-guest-agent
- for i in {1..60}; do
- if [ -s $CMDLINE ]; then
- log_it "Received a new non-empty cmdline file from qemu-guest-agent"
- # Remove old configuration files in /etc/cloudstack if VR is booted from cloudstack
- rm -rf /etc/cloudstack/*.json
- log_it "Booting from cloudstack, remove old configuration files in /etc/cloudstack/"
- break
- fi
- sleep 1
- done
- if [ ! -s $CMDLINE ]; then
- log_it "Failed to receive the cmdline file via the qemu-guest-agent"
- fi
- ;;
- vmware)
- # system time sync'd with host via vmware tools
- systemctl stop ntpd
- systemctl disable ntpd
- systemctl enable open-vm-tools
- systemctl start open-vm-tools
-
- vmtoolsd --cmd 'machine.id.get' > $CMDLINE
- ;;
- virtualpc|hyperv)
- # Hyper-V is recognized as virtualpc hypervisor type. Boot args are passed using KVP Daemon
- systemctl enable hyperv-daemons.hv-fcopy-daemon.service hyperv-daemons.hv-kvp-daemon.service hyperv-daemons.hv-vss-daemon.service
- systemctl start hyperv-daemons.hv-fcopy-daemon.service hyperv-daemons.hv-kvp-daemon.service hyperv-daemons.hv-vss-daemon.service
- sleep 5
- cp -f /var/opt/hyperv/.kvp_pool_0 $CMDLINE
- cat /dev/null > /var/opt/hyperv/.kvp_pool_0
- ;;
- virtualbox)
- # Virtualbox is used to test the virtual router
- # get the commandline from a dmistring (yes, hacky!)
- dmidecode | grep cmdline | sed 's/^.*cmdline://' > $CMDLINE
- RV=$?
- if [ $RV -ne 0 ] ; then
- log_it "Failed to get cmdline from a virtualbox dmi property"
- fi
- ;;
- esac
-
- # Find and export guest type
- export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
-}
-
patch_systemvm() {
local patchfile=$1
local backupfolder="/tmp/.conf.backup"
@@ -158,6 +40,8 @@
fi
rm /usr/local/cloud/systemvm -rf
mkdir -p /usr/local/cloud/systemvm
+ ls -lrt $patchfile
+
echo "All" | unzip $patchfile -d /usr/local/cloud/systemvm >$logfile 2>&1
find /usr/local/cloud/systemvm/ -name \*.sh | xargs chmod 555
if [ -f $backupfolder/cloud.jks ]; then
@@ -171,7 +55,7 @@
}
patch() {
- local PATCH_MOUNT=/media/cdrom
+ local PATCH_MOUNT=/home/cloud
local logfile="/var/log/patchsystemvm.log"
if [ "$TYPE" == "consoleproxy" ] || [ "$TYPE" == "secstorage" ] && [ -f ${PATCH_MOUNT}/agent.zip ] && [ -f /var/cache/cloud/patch.required ]
@@ -188,11 +72,7 @@
rm -f /var/cache/cloud/patch.required
chmod -x /etc/systemd/system/cloud*.service
systemctl daemon-reload
- umount $PATCH_MOUNT || true
- if [ -f /mnt/cmdline ]; then
- cat /mnt/cmdline > $CMDLINE
- fi
return 0
}
@@ -212,11 +92,7 @@
bootstrap() {
log_it "Bootstrapping systemvm appliance"
- export HYPERVISOR=$(hypervisor)
- [ $? -ne 0 ] && log_it "Failed to detect hypervisor type, bailing out" && exit 10
- log_it "Starting guest services for $HYPERVISOR"
-
- config_guest
+ export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
patch
config_sysctl
diff --git a/systemvm/debian/opt/cloud/bin/setup/cksnode.sh b/systemvm/debian/opt/cloud/bin/setup/cksnode.sh
index a864d18..612fdd4 100755
--- a/systemvm/debian/opt/cloud/bin/setup/cksnode.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/cksnode.sh
@@ -39,7 +39,7 @@
log_it "Swap disabled"
log_it "Setting up interfaces"
- setup_common eth0
+# setup_common eth0
setup_system_rfc1918_internal
log_it "Setting up entry in hosts"
diff --git a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
index d0ebd0b..9695b18 100755
--- a/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
+++ b/systemvm/debian/opt/cloud/bin/setup/cloud-early-config
@@ -15,7 +15,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
+set -x
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
# Clear boot up flag, it would be created by rc.local after boot up done
@@ -32,53 +32,61 @@
}
patch() {
- local PATCH_MOUNT=/media/cdrom
+ local PATCH_MOUNT=/home/cloud
local patchfile=$PATCH_MOUNT/cloud-scripts.tgz
local privkey=$PATCH_MOUNT/authorized_keys
local md5file=/var/cache/cloud/cloud-scripts-signature
local cdrom_dev=
mkdir -p $PATCH_MOUNT
- if [ -e /dev/xvdd ]; then
- cdrom_dev=/dev/xvdd
- elif [ -e /dev/cdrom ]; then
- cdrom_dev=/dev/cdrom
- elif [ -e /dev/cdrom1 ]; then
- cdrom_dev=/dev/cdrom1
- elif [ -e /dev/cdrom2 ]; then
- cdrom_dev=/dev/cdrom2
- elif [ -e /dev/cdrom3 ]; then
- cdrom_dev=/dev/cdrom3
- fi
-
if [ -f /var/cache/cloud/authorized_keys ]; then
privkey=/var/cache/cloud/authorized_keys
fi
- if [ -n "$cdrom_dev" ]; then
- mount -o ro $cdrom_dev $PATCH_MOUNT
- local oldmd5=
- [ -f ${md5file} ] && oldmd5=$(cat ${md5file})
- local newmd5=
- [ -f ${patchfile} ] && newmd5=$(md5sum ${patchfile} | awk '{print $1}')
- log_it "Scripts checksum detected: oldmd5=$oldmd5 newmd5=$newmd5"
- if [ "$oldmd5" != "$newmd5" ] && [ -f ${patchfile} ] && [ "$newmd5" != "" ]
- then
- tar xzf $patchfile -C /
- echo ${newmd5} > ${md5file}
- log_it "Patched scripts using $patchfile"
- touch /var/cache/cloud/patch.required
+ retry=60
+ local patched=false
+ while [ $retry -gt 0 ]
+ do
+ if [ -f $patchfile ]; then
+ local oldmd5=
+ [ -f ${md5file} ] && oldmd5=$(cat ${md5file})
+ local newmd5=
+ [ -f ${patchfile} ] && newmd5=$(md5sum ${patchfile} | awk '{print $1}')
+ log_it "Scripts checksum detected: oldmd5=$oldmd5 newmd5=$newmd5"
+ log_it ls -lrt $PATCH_MOUNT
+ if [ "$oldmd5" != "$newmd5" ] && [ -f ${patchfile} ] && [ "$newmd5" != "" ]
+ then
+ tar xzf $patchfile -C /
+ ls -lrt /opt/cloud/bin/keystore*
+ echo ${newmd5} > ${md5file}
+ log_it "Patched scripts using $patchfile"
+ touch /var/cache/cloud/patch.required
+ fi
+
+ if [ -f $privkey ]; then
+ cp -f $privkey /root/.ssh/
+ chmod go-rwx /root/.ssh/authorized_keys
+ fi
+ patched=true
+ break
fi
- if [ -f $privkey ]; then
- cp -f $privkey /root/.ssh/
- chmod go-rwx /root/.ssh/authorized_keys
- fi
+ sleep 2
+ retry=$(($retry-1))
+ log_it "Could not find patch file, retrying"
+ done
+
+ if [ $retry -eq 0 ] && [ "$patched" == "false" ]; then
+ return 2
fi
-
return 0
}
+cleanup() {
+ rm -rf /home/cloud/agent.zip
+ rm -rf /home/cloud/cloud-scripts.tgz
+}
+
start() {
log_it "Executing cloud-early-config"
@@ -99,6 +107,7 @@
patch
sync
/opt/cloud/bin/setup/bootstrap.sh
+ cleanup
log_it "Finished setting up systemvm"
exit 0
diff --git a/systemvm/debian/opt/cloud/bin/setup/common.sh b/systemvm/debian/opt/cloud/bin/setup/common.sh
index 60b8875..7f3d857 100755
--- a/systemvm/debian/opt/cloud/bin/setup/common.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/common.sh
@@ -15,7 +15,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
+set -x
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
. /lib/lsb/init-functions
diff --git a/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh b/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
index 3f00f3d..ec45b7f 100755
--- a/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/consoleproxy.sh
@@ -25,7 +25,6 @@
echo "haproxy dnsmasq apache2 nfs-common portmap" > /var/cache/cloud/disabled_svcs
mkdir -p /var/log/cloud
- setup_common eth0 eth1 eth2
setup_system_rfc1918_internal
log_it "Setting up entry in hosts"
@@ -33,17 +32,6 @@
public_ip=`getPublicIp`
echo "$public_ip $NAME" >> /etc/hosts
- log_it "Applying iptables rules"
- cp /etc/iptables/iptables-consoleproxy /etc/iptables/rules.v4
-
- log_it "Configuring sshd"
- local hyp=$HYPERVISOR
- if [ "$hyp" == "vmware" ] || [ "$hyp" == "hyperv" ]; then
- setup_sshd $ETH1_IP "eth1"
- else
- setup_sshd $ETH0_IP "eth0"
- fi
-
disable_rpfilter
enable_fwding 0
enable_irqbalance 0
diff --git a/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh b/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh
index 9161aeb..0b9e8a7 100755
--- a/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh
@@ -25,7 +25,7 @@
setup_dhcpsrvr() {
log_it "Setting up dhcp server system vm"
- setup_common eth0 eth1
+# setup_common eth0 eth1
setup_dnsmasq
setup_apache2 $ETH0_IP
@@ -36,18 +36,16 @@
enable_irqbalance 0
enable_fwding 0
- cp /etc/iptables/iptables-router /etc/iptables/rules.v4
-
#Only allow DNS service for current network
sed -i "s/-A INPUT -i eth0 -p udp -m udp --dport 53 -j ACCEPT/-A INPUT -i eth0 -p udp -m udp --dport 53 -s $DHCP_RANGE\/$CIDR_SIZE -j ACCEPT/g" /etc/iptables/rules.v4
sed -i "s/-A INPUT -i eth0 -p tcp -m tcp --dport 53 -j ACCEPT/-A INPUT -i eth0 -p tcp -m tcp --dport 53 -s $DHCP_RANGE\/$CIDR_SIZE -j ACCEPT/g" /etc/iptables/rules.v4
- if [ "$SSHONGUEST" == "true" ]
- then
- setup_sshd $ETH0_IP "eth0"
- else
- setup_sshd $ETH1_IP "eth1"
- fi
+# if [ "$SSHONGUEST" == "true" ]
+# then
+# setup_sshd $ETH0_IP "eth0"
+# else
+# setup_sshd $ETH1_IP "eth1"
+# fi
}
dhcpsrvr_svcs
diff --git a/systemvm/debian/opt/cloud/bin/setup/elbvm.sh b/systemvm/debian/opt/cloud/bin/setup/elbvm.sh
index ae16b4b..52132cc 100755
--- a/systemvm/debian/opt/cloud/bin/setup/elbvm.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/elbvm.sh
@@ -25,20 +25,11 @@
setup_elbvm() {
log_it "Setting up Elastic Load Balancer system vm"
- setup_common eth0 eth1
sed -i /$NAME/d /etc/hosts
public_ip=$ETH2_IP
[ "$ETH2_IP" == "0.0.0.0" ] || [ "$ETH2_IP" == "" ] && public_ip=$ETH0_IP
echo "$public_ip $NAME" >> /etc/hosts
- cp /etc/iptables/iptables-elbvm /etc/iptables/rules.v4
- if [ "$SSHONGUEST" == "true" ]
- then
- setup_sshd $ETH0_IP "eth0"
- else
- setup_sshd $ETH1_IP "eth1"
- fi
-
enable_fwding 0
enable_irqbalance 0
}
diff --git a/systemvm/debian/opt/cloud/bin/setup/ilbvm.sh b/systemvm/debian/opt/cloud/bin/setup/ilbvm.sh
index ac801b2..83cc855 100755
--- a/systemvm/debian/opt/cloud/bin/setup/ilbvm.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/ilbvm.sh
@@ -25,7 +25,7 @@
setup_ilbvm() {
log_it "Setting up Internal Load Balancer system vm"
- setup_common eth0 eth1
+# setup_common eth0 eth1
#eth0 = guest network, eth1=control network
sed -i /$NAME/d /etc/hosts
diff --git a/systemvm/debian/opt/cloud/bin/setup/init.sh b/systemvm/debian/opt/cloud/bin/setup/init.sh
new file mode 100644
index 0000000..5923b35
--- /dev/null
+++ b/systemvm/debian/opt/cloud/bin/setup/init.sh
@@ -0,0 +1,217 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -x
+PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+CMDLINE=/var/cache/cloud/cmdline
+
+hypervisor() {
+ if [ -d /proc/xen ]; then
+ mount -t xenfs none /proc/xen
+ $(dmesg | grep -q "Xen HVM")
+ if [ $? -eq 0 ]; then # 1=PV,0=HVM
+ echo "xen-hvm" && return 0
+ else
+ echo "xen-pv" && return 0
+ fi
+ fi
+
+ [ -x /usr/sbin/virt-what ] && local facts=( $(virt-what) )
+ if [ "$facts" != "" ]; then
+ # Xen HVM is recognized as Hyperv when Viridian extensions are enabled
+ if [ "${facts[-1]}" == "xen-domU" ] && [ "${facts[0]}" == "hyperv" ]; then
+ echo "xen-hvm" && return 0
+ else
+ echo ${facts[-1]} && return 0
+ fi
+ fi
+
+ grep -q QEMU /proc/cpuinfo && echo "kvm" && return 0
+ grep -q QEMU /var/log/messages && echo "kvm" && return 0
+
+ vmware-checkvm &> /dev/null && echo "vmware" && return 0
+
+ echo "unknown" && return 1
+}
+
+config_guest() {
+ [ ! -d /proc/xen ] && sed -i 's/^vc/#vc/' /etc/inittab && telinit q
+ [ -d /proc/xen ] && sed -i 's/^#vc/vc/' /etc/inittab && telinit q
+
+ systemctl daemon-reload
+
+ case $HYPERVISOR in
+ xen-pv|xen-domU)
+ systemctl stop ntpd
+ systemctl disable ntpd
+ systemctl enable xe-daemon
+ systemctl start xe-daemon
+
+ cat /proc/cmdline > $CMDLINE
+ sed -i "s/%/ /g" $CMDLINE
+ ;;
+ xen-hvm)
+ systemctl stop ntpd
+ systemctl disable ntpd
+ systemctl enable xe-daemon
+ systemctl start xe-daemon
+
+ if [ ! -f /usr/bin/xenstore-read ]; then
+ log_it "ERROR: xentools not installed, cannot found xenstore-read" && exit 5
+ fi
+ /usr/bin/xenstore-read vm-data/cloudstack/init > $CMDLINE
+ sed -i "s/%/ /g" $CMDLINE
+ ;;
+ kvm)
+ # Configure kvm hotplug support
+ if grep -E 'CONFIG_HOTPLUG_PCI=y|CONFIG_HOTPLUG_PCI_ACPI=y' /boot/config-`uname -r`; then
+ log_it "acpiphp and pci_hotplug module already compiled in"
+ else
+ modprobe acpiphp 2> /dev/null && log_it "acpiphp module loaded" || true
+ modprobe pci_hotplug 2> /dev/null && log_it "pci_hotplug module loaded" || true
+ fi
+
+ sed -i -e "/^s0:2345:respawn.*/d" /etc/inittab
+ sed -i -e "/6:23:respawn/a\s0:2345:respawn:/sbin/getty -L 115200 ttyS0 vt102" /etc/inittab
+ systemctl enable qemu-guest-agent
+ systemctl start qemu-guest-agent
+
+ # Wait for $CMDLINE file to be written by the qemu-guest-agent
+ for i in {1..60}; do
+ if [ -s $CMDLINE ]; then
+ log_it "Received a new non-empty cmdline file from qemu-guest-agent"
+ # Remove old configuration files in /etc/cloudstack if VR is booted from cloudstack
+ rm -rf /etc/cloudstack/*.json
+ log_it "Booting from cloudstack, remove old configuration files in /etc/cloudstack/"
+ break
+ fi
+ sleep 1
+ done
+ if [ ! -s $CMDLINE ]; then
+ log_it "Failed to receive the cmdline file via the qemu-guest-agent"
+ fi
+ ;;
+ vmware)
+ # system time sync'd with host via vmware tools
+ systemctl stop ntpd
+ systemctl disable ntpd
+ systemctl enable open-vm-tools
+ systemctl start open-vm-tools
+
+ vmtoolsd --cmd 'machine.id.get' > $CMDLINE
+ ;;
+ virtualpc|hyperv)
+ # Hyper-V is recognized as virtualpc hypervisor type. Boot args are passed using KVP Daemon
+ systemctl enable hyperv-daemons.hv-fcopy-daemon.service hyperv-daemons.hv-kvp-daemon.service hyperv-daemons.hv-vss-daemon.service
+ systemctl start hyperv-daemons.hv-fcopy-daemon.service hyperv-daemons.hv-kvp-daemon.service hyperv-daemons.hv-vss-daemon.service
+ sleep 5
+ cp -f /var/opt/hyperv/.kvp_pool_0 $CMDLINE
+ cat /dev/null > /var/opt/hyperv/.kvp_pool_0
+ ;;
+ virtualbox)
+ # Virtualbox is used to test the virtual router
+ # get the commandline from a dmistring (yes, hacky!)
+ dmidecode | grep cmdline | sed 's/^.*cmdline://' > $CMDLINE
+ RV=$?
+ if [ $RV -ne 0 ] ; then
+ log_it "Failed to get cmdline from a virtualbox dmi property"
+ fi
+ ;;
+ esac
+
+ if [ -f /mnt/cmdline ]; then
+ cat /mnt/cmdline > $CMDLINE
+ fi
+
+ # Find and export guest type
+ export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
+}
+
+setup_interface_sshd() {
+
+ if [ "$TYPE" != "cksnode" ]; then
+ log_it "Applying iptables rules"
+ if [ "$TYPE" != "dhcpsrvr" ]; then
+ cp /etc/iptables/iptables-$TYPE /etc/iptables/rules.v4
+ else
+ cp /etc/iptables/iptables-router /etc/iptables/rules.v4
+ fi
+ fi
+
+ if [ "$TYPE" == "consoleproxy" ] || [ "$TYPE" == "secstorage" ]; then
+ setup_common eth0 eth1 eth2
+ log_it "Configuring sshd"
+ local hyp=$HYPERVISOR
+ if [ "$hyp" == "vmware" ] || [ "$hyp" == "hyperv" ]; then
+ setup_sshd $ETH1_IP "eth1"
+ else
+ setup_sshd $ETH0_IP "eth0"
+ fi
+
+ elif [ "$TYPE" == "router" ]; then
+ if [ -n "$ETH2_IP" ]; then
+ setup_common eth0 eth1 eth2
+
+ if [ -n "$EXTRA_PUBNICS" ]; then
+ for ((i = 3; i < 3 + $EXTRA_PUBNICS; i++)); do
+ setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
+ done
+ fi
+ else
+ setup_common eth0 eth1
+ if [ -n "$EXTRA_PUBNICS" ]; then
+ for ((i = 2; i < 2 + $EXTRA_PUBNICS; i++)); do
+ setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
+ done
+ fi
+ fi
+ setup_sshd $ETH1_IP "eth1"
+
+ elif [ "$TYPE" == "vpcrouter" ]; then
+ setup_interface "0" $ETH0_IP $ETH0_MASK $GW
+ setup_sshd $ETH0_IP "eth0"
+
+ elif [ "$TYPE" == "ilbvm" ]; then
+ setup_common eth0 eth1
+ setup_sshd $ETH1_IP "eth1"
+
+ elif [ "$TYPE" == "elbvm" ] || [ "$TYPE" == "dhcpsrvr"]; then
+ setup_common eth0 eth1
+ if [ "$SSHONGUEST" == "true" ]; then
+ setup_sshd $ETH0_IP "eth0"
+ else
+ setup_sshd $ETH1_IP "eth1"
+ fi
+ elif [ "$TYPE" == "cksnode" ]; then
+ setup_common eth0
+ fi
+
+ systemctl restart systemd-journald
+ # Patch known systemd/sshd memory leak - https://github.com/systemd/systemd/issues/8015#issuecomment-476160981
+ echo '@include null' >> /etc/pam.d/systemd-user
+ # Enable and Start SSH
+ systemctl enable --now --no-block ssh
+}
+
+export HYPERVISOR=$(hypervisor)
+[ $? -ne 0 ] && log_it "Failed to detect hypervisor type, bailing out" && exit 10
+log_it "Starting guest services for $HYPERVISOR"
+
+config_guest
+source /opt/cloud/bin/setup/common.sh
+setup_interface_sshd
\ No newline at end of file
diff --git a/systemvm/debian/opt/cloud/bin/setup/postinit.sh b/systemvm/debian/opt/cloud/bin/setup/postinit.sh
index 0492930..ba5c394 100755
--- a/systemvm/debian/opt/cloud/bin/setup/postinit.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/postinit.sh
@@ -23,17 +23,11 @@
log_action_msg "$@"
}
-# Eject cdrom if any
-CMDLINE=/var/cache/cloud/cmdline
-export TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
-if [ "$TYPE" != "cksnode" ]; then
- eject || true
-fi
-
# Restart journald for setting changes to apply
systemctl restart systemd-journald
-TYPE=$(grep -Po 'type=\K[a-zA-Z]*' /var/cache/cloud/cmdline)
+CMDLINE=/var/cache/cloud/cmdline
+TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
if [ "$TYPE" == "router" ] || [ "$TYPE" == "vpcrouter" ] || [ "$TYPE" == "dhcpsrvr" ]
then
if [ -x /opt/cloud/bin/update_config.py ]
@@ -71,10 +65,4 @@
ip6tables-restore < $ipv6
fi
-# Patch known systemd/sshd memory leak - https://github.com/systemd/systemd/issues/8015#issuecomment-476160981
-echo '@include null' >> /etc/pam.d/systemd-user
-
-# Enable and Start SSH
-systemctl enable --now --no-block ssh
-
date > /var/cache/cloud/boot_up_done
diff --git a/systemvm/debian/opt/cloud/bin/setup/router.sh b/systemvm/debian/opt/cloud/bin/setup/router.sh
index e8f6edf..d7113c4 100755
--- a/systemvm/debian/opt/cloud/bin/setup/router.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/router.sh
@@ -43,23 +43,6 @@
oldmd5=
[ -f "/etc/udev/rules.d/70-persistent-net.rules" ] && oldmd5=$(md5sum "/etc/udev/rules.d/70-persistent-net.rules" | awk '{print $1}')
- if [ -n "$ETH2_IP" ]; then
- setup_common eth0 eth1 eth2
-
- if [ -n "$EXTRA_PUBNICS" ]; then
- for ((i = 3; i < 3 + $EXTRA_PUBNICS; i++)); do
- setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
- done
- fi
- else
- setup_common eth0 eth1
- if [ -n "$EXTRA_PUBNICS" ]; then
- for ((i = 2; i < 2 + $EXTRA_PUBNICS; i++)); do
- setup_interface "$i" "0.0.0.0" "255.255.255.255" $GW "force"
- done
- fi
- fi
-
log_it "Checking udev NIC assignment order changes"
if [ "$NIC_MACS" != "" ]
then
@@ -88,8 +71,6 @@
enable_fwding 1
enable_rpsrfs 1
enable_passive_ftp 1
- cp /etc/iptables/iptables-router /etc/iptables/rules.v4
- setup_sshd $ETH1_IP "eth1"
# Only allow DNS service for current network
sed -i "s/-A INPUT -i eth0 -p udp -m udp --dport 53 -j ACCEPT/-A INPUT -i eth0 -p udp -m udp --dport 53 -s $DHCP_RANGE\/$CIDR_SIZE -j ACCEPT/g" /etc/iptables/rules.v4
diff --git a/systemvm/debian/opt/cloud/bin/setup/secstorage.sh b/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
index 13ed5c5..3b21ed5 100755
--- a/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/secstorage.sh
@@ -25,7 +25,6 @@
echo "conntrackd keepalived haproxy dnsmasq" > /var/cache/cloud/disabled_svcs
mkdir -p /var/log/cloud
- setup_common eth0 eth1 eth2
setup_storage_network
setup_system_rfc1918_internal
@@ -37,14 +36,6 @@
log_it "Applying iptables rules"
cp /etc/iptables/iptables-secstorage /etc/iptables/rules.v4
- log_it "Configuring sshd"
- local hyp=$HYPERVISOR
- if [ "$hyp" == "vmware" ] || [ "$hyp" == "hyperv" ]; then
- setup_sshd $ETH1_IP "eth1"
- else
- setup_sshd $ETH0_IP "eth0"
- fi
-
log_it "Configuring apache2"
setup_apache2 $ETH2_IP
diff --git a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
index f97fb16..ba4af90 100755
--- a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
+++ b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh
@@ -29,7 +29,6 @@
auto lo eth0
iface lo inet loopback
EOF
- setup_interface "0" $ETH0_IP $ETH0_MASK $GW
echo $NAME > /etc/hostname
echo 'AVAHI_DAEMON_DETECT_LOCAL=0' > /etc/default/avahi-daemon
@@ -86,7 +85,7 @@
enable_fwding 1
enable_passive_ftp 1
cp /etc/iptables/iptables-vpcrouter /etc/iptables/rules.v4
- setup_sshd $ETH0_IP "eth0"
+# setup_sshd $ETH0_IP "eth0"
cp /etc/vpcdnsmasq.conf /etc/dnsmasq.conf
cp /etc/cloud-nic.rules /etc/udev/rules.d/cloud-nic.rules
echo "" > /etc/dnsmasq.d/dhcphosts.txt
diff --git a/systemvm/patch-sysvms.sh b/systemvm/patch-sysvms.sh
new file mode 100644
index 0000000..cf0b452
--- /dev/null
+++ b/systemvm/patch-sysvms.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+backupfolder=/tmp/bkpup_live_patch
+logfile="/var/log/livepatchsystemvm.log"
+newpath="/home/cloud/"
+CMDLINE=/var/cache/cloud/cmdline
+md5file=/var/cache/cloud/cloud-scripts-signature
+svcfile=/var/cache/cloud/enabled_svcs
+TYPE=$(grep -Po 'type=\K[a-zA-Z]*' $CMDLINE)
+patchfailed=0
+
+
+backup_old_package() {
+ mkdir -p $backupfolder
+ echo "Backing up keystore file and certificates" > $logfile
+ mkdir -p $backupfolder/conf
+ cp -r /usr/local/cloud/systemvm/conf/* $backupfolder/conf
+ echo "Backing up agent package" >> $logfile
+ zip -r $backupfolder/agent.zip /usr/local/cloud/systemvm/* >> $logfile 2>&1
+ cp $md5file $backupfolder
+ echo "Backing up cloud-scripts file" >> $logfile
+ tar -zcvf $backupfolder/cloud-scripts.tgz /etc/ /var/ /opt/ /root/ >> $logfile 2>&1
+}
+
+restore_backup() {
+ echo "Restoring cloud scripts" >> $logfile
+ tar -xvf $backupfolder/cloud-scripts.tar -C / >> $logfile 2>&1
+ echo "Restoring agent package" >> $logfile
+ unzip $backupfolder/agent.zip -d /usr/local/cloud/systemvm/ >> $logfile 2>&1
+ echo "Restore keystore file and certificates"
+ mkdir -p "/usr/local/cloud/systemvm/conf/"
+ cp -r $backupfolder/conf/* /usr/local/cloud/systemvm/conf/
+ restart_services
+ cp $backupfolder/cloud-scripts-signature $md5file
+}
+
+update_checksum() {
+ newmd5=$(md5sum $1 | awk '{print $1}')
+ echo "checksum: " ${newmd5} >> $logfile
+ echo ${newmd5} > ${md5file}
+}
+
+restart_services() {
+ systemctl daemon-reload
+ while IFS= read -r line
+ do
+ echo "$line"
+ systemctl restart "$line"
+ sleep 5
+ systemctl is-active --quiet "$line"
+ if [ $? -gt 0 ]; then
+ echo "Failed to start "$line" service. Patch Failed. Restoring backup" >> $logfile
+ restore_backup
+ patchfailed=1
+ break
+ fi
+ done < "$svcfile"
+}
+
+cleanup_systemVM() {
+ rm -rf $backupfolder
+ rm -rf "$newpath""cloud-scripts.tgz" "$newpath""agent.zip" "$newpath""patch-sysvms.sh"
+}
+
+patch_systemvm() {
+ rm -rf /usr/local/cloud/systemvm
+ mkdir -p /usr/local/cloud/systemvm
+ echo "All" | unzip $newpath/agent.zip -d /usr/local/cloud/systemvm >> $logfile 2>&1
+ find /usr/local/cloud/systemvm/ -name \*.sh | xargs chmod 555
+
+ echo "Extracting cloud scripts" >> $logfile
+ tar -xvf $newpath/cloud-scripts.tgz -C / >> $logfile 2>&1
+
+ if [ -f $backupfolder/conf/cloud.jks ]; then
+ cp -r $backupfolder/conf/* /usr/local/cloud/systemvm/conf/
+ echo "Restored keystore file and certs using backup" >> $logfile
+ fi
+
+ update_checksum $newpath/cloud-scripts.tgz
+
+ if [ "$TYPE" == "consoleproxy" ] || [ "$TYPE" == "secstorage" ] || [[ "$TYPE" == *router ]]; then
+ restart_services
+ fi
+}
+
+
+backup_old_package
+patch_systemvm
+cleanup_systemVM
+
+exit $patchfailed
diff --git a/systemvm/pom.xml b/systemvm/pom.xml
index 21b95d2..ef2e540 100644
--- a/systemvm/pom.xml
+++ b/systemvm/pom.xml
@@ -88,6 +88,12 @@
<include>agent.zip</include>
</includes>
</resource>
+ <resource>
+ <directory>${basedir}</directory>
+ <includes>
+ <include>patch-sysvms.sh</include>
+ </includes>
+ </resource>
</resources>
</configuration>
</execution>
diff --git a/tools/appliance/build.sh b/tools/appliance/build.sh
index 1c83f9a..79de31a 100755
--- a/tools/appliance/build.sh
+++ b/tools/appliance/build.sh
@@ -349,10 +349,10 @@
# process the disk at dist
kvm_export
- ovm_export
- xen_server_export
- vmware_export
- hyperv_export
+# ovm_export
+# xen_server_export
+# vmware_export
+# hyperv_export
rm -f "dist/${appliance}"
cd dist && chmod +r * && cd ..
cd dist && md5sum * > md5sum.txt && cd ..
diff --git a/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh b/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh
index db3eec5..8cdfce7 100644
--- a/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh
+++ b/tools/appliance/systemvmtemplate/scripts/configure_systemvm_services.sh
@@ -68,6 +68,7 @@
chmod -x /etc/systemd/system/* || true
systemctl daemon-reload
+ systemctl enable cloud-preinit
systemctl enable cloud-early-config
systemctl enable cloud-postinit
}
diff --git a/tools/appliance/systemvmtemplate/template.json b/tools/appliance/systemvmtemplate/template.json
index bd932bf..46fdbc9 100644
--- a/tools/appliance/systemvmtemplate/template.json
+++ b/tools/appliance/systemvmtemplate/template.json
@@ -27,8 +27,8 @@
"format": "qcow2",
"headless": true,
"http_directory": "http",
- "iso_checksum": "sha512:5f6aed67b159d7ccc1a90df33cc8a314aa278728a6f50707ebf10c02e46664e383ca5fa19163b0a1c6a4cb77a39587881584b00b45f512b4a470f1138eaa1801",
- "iso_url": "https://cdimage.debian.org/debian-cd/11.0.0/amd64/iso-cd/debian-11.0.0-amd64-netinst.iso",
+ "iso_checksum": "sha512:02257c3ec27e45d9f022c181a69b59da67e5c72871cdb4f9a69db323a1fad58093f2e69702d29aa98f5f65e920e0b970d816475a5a936e1f3bf33832257b7e92",
+ "iso_url": "https://cdimage.debian.org/debian-cd/11.1.0/amd64/iso-cd/debian-11.1.0-amd64-netinst.iso",
"net_device": "virtio-net",
"output_directory": "../dist",
"qemuargs": [
diff --git a/utils/src/main/java/com/cloud/utils/EncryptionUtil.java b/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
index b82842e..ff791a3 100644
--- a/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
+++ b/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
@@ -18,7 +18,12 @@
*/
package com.cloud.utils;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -26,6 +31,7 @@
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
import org.apache.log4j.Logger;
import org.jasypt.encryption.pbe.PBEStringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
@@ -70,4 +76,14 @@
throw new CloudRuntimeException("unable to generate signature", e);
}
}
+
+ public static String calculateChecksum(File file) {
+ try (InputStream is = Files.newInputStream(Paths.get(file.getPath()))) {
+ return DigestUtils.md5Hex(is);
+ } catch (IOException e) {
+ String errMsg = "Failed to calculate template checksum";
+ s_logger.error(errMsg, e);
+ throw new CloudRuntimeException(errMsg, e);
+ }
+ }
}
diff --git a/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java b/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java
index d5cd91af..6625864 100644
--- a/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java
+++ b/utils/src/main/java/com/cloud/utils/ssh/SshHelper.java
@@ -52,6 +52,12 @@
scpTo(host, port, user, pemKeyFile, password, remoteTargetDirectory, localFile, fileMode, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT);
}
+ public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String[] localFiles, String fileMode)
+ throws Exception {
+
+ scpTo(host, port, user, pemKeyFile, password, remoteTargetDirectory, localFiles, fileMode, DEFAULT_CONNECT_TIMEOUT, DEFAULT_KEX_TIMEOUT);
+ }
+
public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String remoteFileName,
String fileMode) throws Exception {
@@ -118,6 +124,42 @@
}
}
+ public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, String[] localFiles, String fileMode,
+ int connectTimeoutInMs, int kexTimeoutInMs) throws Exception {
+
+ com.trilead.ssh2.Connection conn = null;
+ com.trilead.ssh2.SCPClient scpClient = null;
+
+ try {
+ conn = new com.trilead.ssh2.Connection(host, port);
+ conn.connect(null, connectTimeoutInMs, kexTimeoutInMs);
+
+ if (pemKeyFile == null) {
+ if (!conn.authenticateWithPassword(user, password)) {
+ String msg = "Failed to authentication SSH user " + user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ } else {
+ if (!conn.authenticateWithPublicKey(user, pemKeyFile, password)) {
+ String msg = "Failed to authentication SSH user " + user + " on host " + host;
+ s_logger.error(msg);
+ throw new Exception(msg);
+ }
+ }
+
+ scpClient = conn.createSCPClient();
+
+ if (fileMode != null)
+ scpClient.put(localFiles, remoteTargetDirectory, fileMode);
+ else
+ scpClient.put(localFiles, remoteTargetDirectory);
+ } finally {
+ if (conn != null)
+ conn.close();
+ }
+ }
+
public static void scpTo(String host, int port, String user, File pemKeyFile, String password, String remoteTargetDirectory, byte[] data, String remoteFileName,
String fileMode, int connectTimeoutInMs, int kexTimeoutInMs) throws Exception {