This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch test-opt in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit 80915815f78a7f40fb0819a1a59e81fb10b5c052 Author: Pearl Dsilva <[email protected]> AuthorDate: Tue Dec 7 13:09:16 2021 +0530 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 --- api/src/main/java/com/cloud/event/EventTypes.java | 4 + .../java/com/cloud/server/ManagementService.java | 2 + .../command/admin/systemvm/PatchSystemVMCmd.java | 107 +++++++++++++++ .../com/cloud/agent/api/PatchSystemVmAnswer.java | 44 ++++++ .../com/cloud/agent/api/PatchSystemVmCommand.java | 29 ++++ debian/rules | 3 +- engine/schema/pom.xml | 9 +- .../com/cloud/upgrade/DatabaseUpgradeChecker.java | 9 +- .../upgrade/SystemVmTemplateRegistration.java | 22 +-- .../resources/META-INF/db/schema-41600to41610.sql | 4 +- engine/schema/templateConfig.sh | 20 +-- packaging/centos7/cloud.spec | 7 +- packaging/centos8/cloud.spec | 7 +- packaging/suse15/cloud.spec | 7 +- .../kvm/resource/LibvirtComputingResource.java | 10 +- .../LibvirtPatchSystemVmCommandWrapper.java | 132 ++++++++++++++++++ .../wrapper/LibvirtStartCommandWrapper.java | 21 +++ .../kvm/resource/LibvirtComputingResourceTest.java | 11 +- pom.xml | 1 + scripts/vm/hypervisor/xenserver/xcpserver/patch | 2 + .../com/cloud/api/ResponseObjectTypeAdapter.java | 4 + .../com/cloud/server/ManagementServerImpl.java | 90 ++++++++++++- .../etc/systemd/system/cloud-early-config.service | 7 +- .../etc/systemd/system/cloud-postinit.service | 2 +- ...-early-config.service => cloud-preinit.service} | 4 +- systemvm/debian/opt/cloud/bin/setup/bootstrap.sh | 134 +------------------ systemvm/debian/opt/cloud/bin/setup/cksnode.sh | 2 +- .../debian/opt/cloud/bin/setup/cloud-early-config | 73 +++++----- systemvm/debian/opt/cloud/bin/setup/common.sh | 2 +- .../debian/opt/cloud/bin/setup/consoleproxy.sh | 12 -- systemvm/debian/opt/cloud/bin/setup/dhcpsrvr.sh | 16 +-- systemvm/debian/opt/cloud/bin/setup/elbvm.sh | 9 -- systemvm/debian/opt/cloud/bin/setup/ilbvm.sh | 2 +- .../opt/cloud/bin/setup/{bootstrap.sh => init.sh} | 147 +++++++++------------ systemvm/debian/opt/cloud/bin/setup/postinit.sh | 16 +-- systemvm/debian/opt/cloud/bin/setup/router.sh | 19 --- systemvm/debian/opt/cloud/bin/setup/secstorage.sh | 9 -- systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh | 3 +- systemvm/patch-sysvms.sh | 108 +++++++++++++++ systemvm/pom.xml | 6 + tools/appliance/build.sh | 8 +- .../scripts/configure_systemvm_services.sh | 1 + tools/appliance/systemvmtemplate/template.json | 4 +- .../main/java/com/cloud/utils/EncryptionUtil.java | 16 +++ .../main/java/com/cloud/utils/ssh/SshHelper.java | 42 ++++++ 45 files changed, 803 insertions(+), 384 deletions(-) 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 @@ public class EventTypes { // 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 @@ public class EventTypes { 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.ListCapacityCmd; 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 @@ public interface ManagementService { 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 @@ override_dh_auto_install: 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 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { 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.template.VirtualMachineTemplate; 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.ImageStoreDaoImpl; 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.BufferedReader; 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 @@ public class SystemVmTemplateRegistration { } } - 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 class SystemVmTemplateRegistration { } } - public static void parseMetadataFile() { + public static String parseMetadataFile() { try { Ini ini = new Ini(); ini.load(new FileReader(METADATA_FILE)); @@ -702,6 +691,8 @@ public class SystemVmTemplateRegistration { 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 @@ public class SystemVmTemplateRegistration { } 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 @@ public class SystemVmTemplateRegistration { 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 @@ function getTemplateVersion() { 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 getChecksum() { 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 @@ function createMetadataFile() { 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}%{_datadir}/%{name}-common/vms 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 @@ pip3 install --upgrade urllib3 %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}%{_datadir}/%{name}-common/vms 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 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %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}%{_datadir}/%{name}-common/vms 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 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz %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 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv 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 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv s_powerStatesTable.put(DomainState.VIR_DOMAIN_SHUTDOWN, PowerState.PowerOff); } - private VirtualRoutingResource _virtRouterResource; + public VirtualRoutingResource _virtRouterResource; private String _pingTestPath; @@ -463,7 +467,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv 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 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv 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 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman 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.XPathConstants; 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.apache.cloudstack.utils.bytescale.ByteScaleUtils; 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 @@ public class LibvirtComputingResourceTest { } @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 @@ public class LibvirtComputingResourceTest { } @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 @@ vmops=..,0755,/etc/xapi.d/plugins 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 java.lang.reflect.Type; 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 @@ public class ResponseObjectTypeAdapter implements JsonSerializer<ResponseObject> 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.crypto.spec.SecretKeySpec; 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.swift.ListSwiftsCmd; 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private StoragePoolJoinDao _poolJoinDao; @Inject - private NetworkDao _networkDao; + private NetworkDao networkDao; @Inject private StorageManager _storageMgr; @Inject @@ -896,6 +905,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe _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-early-config.service b/systemvm/debian/etc/systemd/system/cloud-preinit.service similarity index 67% copy from systemvm/debian/etc/systemd/system/cloud-early-config.service copy to systemvm/debian/etc/systemd/system/cloud-preinit.service index 2af5276..373cd9e 100644 --- a/systemvm/debian/etc/systemd/system/cloud-early-config.service +++ b/systemvm/debian/etc/systemd/system/cloud-preinit.service @@ -1,5 +1,5 @@ [Unit] -Description=CloudStack post-boot patching service using cmdline +Description=CloudStack service to initialize interfaces DefaultDependencies=no Before=network-pre.target @@ -13,6 +13,6 @@ WantedBy=multi-user.target [Service] Type=oneshot -ExecStart=/opt/cloud/bin/setup/cloud-early-config +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_it() { 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 @@ patch_systemvm() { 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_systemvm() { } 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 @@ patch() { 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 @@ config_sysctl() { 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 @@ setup_k8s_node() { 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 @@ log_it() { } 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 - 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 @@ start() { 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 @@ setup_console_proxy() { 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 @@ setup_console_proxy() { 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 @@ dhcpsrvr_svcs() { 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 @@ setup_dhcpsrvr() { 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 @@ elbvm_svcs() { 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 @@ ilbvm_svcs() { 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/bootstrap.sh b/systemvm/debian/opt/cloud/bin/setup/init.sh old mode 100755 new mode 100644 similarity index 67% copy from systemvm/debian/opt/cloud/bin/setup/bootstrap.sh copy to systemvm/debian/opt/cloud/bin/setup/init.sh index 2335d64..5923b35 --- a/systemvm/debian/opt/cloud/bin/setup/bootstrap.sh +++ b/systemvm/debian/opt/cloud/bin/setup/init.sh @@ -16,19 +16,10 @@ # 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 -rm -f /var/cache/cloud/enabled_svcs -rm -f /var/cache/cloud/disabled_svcs - -. /lib/lsb/init-functions - -log_it() { - echo "$(date) $@" >> /var/log/cloud.log - log_action_msg "$@" -} - hypervisor() { if [ -d /proc/xen ]; then mount -t xenfs none /proc/xen @@ -143,92 +134,84 @@ config_guest() { ;; 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) } -patch_systemvm() { - local patchfile=$1 - local backupfolder="/tmp/.conf.backup" - local logfile="/var/log/patchsystemvm.log" - if [ -f /usr/local/cloud/systemvm/conf/cloud.jks ]; then - rm -fr $backupfolder - mkdir -p $backupfolder - cp -r /usr/local/cloud/systemvm/conf/* $backupfolder/ - fi - rm /usr/local/cloud/systemvm -rf - mkdir -p /usr/local/cloud/systemvm - 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 - cp -r $backupfolder/* /usr/local/cloud/systemvm/conf/ - echo "Restored keystore file and certs using backup" >> $logfile - fi - rm -fr $backupfolder - # Import global cacerts into 'cloud' service's keystore - keytool -importkeystore -srckeystore /etc/ssl/certs/java/cacerts -destkeystore /usr/local/cloud/systemvm/certs/realhostip.keystore -srcstorepass changeit -deststorepass vmops.com -noprompt || true - return 0 -} +setup_interface_sshd() { -patch() { - local PATCH_MOUNT=/media/cdrom - local logfile="/var/log/patchsystemvm.log" - - if [ "$TYPE" == "consoleproxy" ] || [ "$TYPE" == "secstorage" ] && [ -f ${PATCH_MOUNT}/agent.zip ] && [ -f /var/cache/cloud/patch.required ] - then - echo "Patching systemvm for cloud service with mount=$PATCH_MOUNT for type=$TYPE" >> $logfile - patch_systemvm ${PATCH_MOUNT}/agent.zip - if [ $? -gt 0 ] - then - echo "Failed to apply patch systemvm\n" >> $logfile - exit 1 + 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 - 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 -} - -config_sysctl() { - # When there is more memory reset the cache back pressure to default 100 - physmem=$(free|awk '/^Mem:/{print $2}') - if [ $((physmem)) -lt 409600 ]; then - sed -i "/^vm.vfs_cache_pressure/ c\vm.vfs_cache_pressure = 200" /etc/sysctl.conf - else - sed -i "/^vm.vfs_cache_pressure/ c\vm.vfs_cache_pressure = 100" /etc/sysctl.conf - 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 - sync - sysctl -p -} + elif [ "$TYPE" == "router" ]; then + if [ -n "$ETH2_IP" ]; then + setup_common eth0 eth1 eth2 -bootstrap() { - log_it "Bootstrapping systemvm appliance" + 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" - export HYPERVISOR=$(hypervisor) - [ $? -ne 0 ] && log_it "Failed to detect hypervisor type, bailing out" && exit 10 - log_it "Starting guest services for $HYPERVISOR" + elif [ "$TYPE" == "vpcrouter" ]; then + setup_interface "0" $ETH0_IP $ETH0_MASK $GW + setup_sshd $ETH0_IP "eth0" - config_guest - patch - config_sysctl + elif [ "$TYPE" == "ilbvm" ]; then + setup_common eth0 eth1 + setup_sshd $ETH1_IP "eth1" - log_it "Configuring systemvm type=$TYPE" - if [ -f "/opt/cloud/bin/setup/$TYPE.sh" ]; then - /opt/cloud/bin/setup/$TYPE.sh - else - /opt/cloud/bin/setup/default.sh + 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 - log_it "Finished setting up systemvm" - exit 0 + 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 } -bootstrap +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_it() { 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 @@ then 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 @@ setup_router() { 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 @@ setup_router() { 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 @@ setup_secstorage() { 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 @@ setup_secstorage() { 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 @@ setup_vpcrouter() { 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 @@ EOF 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 @@ function main() { # 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 @@ function install_cloud_scripts() { 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.Mac; 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 @@ public class EncryptionUtil { 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 @@ public class SshHelper { 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 class SshHelper { } } + 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 {
