blob: dceb8e07b07db20716639da376a80c5c11159f0b [file] [log] [blame]
// 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.upgrade;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.utils.security.DigestHelper;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.cpu.CPU;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.utils.HttpUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.UriUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
@RunWith(MockitoJUnitRunner.class)
public class SystemVmTemplateRegistrationTest {
@Mock
ClusterDao clusterDao;
@Mock
VMTemplateDao vmTemplateDao;
@Spy
@InjectMocks
SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration();
private void setupMetadataFile(MockedStatic<SystemVmTemplateRegistration> mockedStatic, String content) {
try {
String location = "metadata.ini";
if (StringUtils.isNotBlank(content)) {
File tempFile = File.createTempFile("metadata", ".ini");
location = tempFile.getAbsolutePath();
Files.write(Paths.get(location), content.getBytes());
tempFile.deleteOnExit();
}
mockedStatic.when(SystemVmTemplateRegistration::getMetadataFilePath).thenReturn(location);
} catch (Exception e) {
fail(e.getMessage());
}
}
@Test
public void test_parseMetadataFile_noFile() {
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, null);
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class,
SystemVmTemplateRegistration::parseMetadataFile);
assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file"));
}
}
@Test
public void test_parseMetadataFile_invalidContent() {
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, "abc");
CloudRuntimeException exception = assertThrows(CloudRuntimeException.class,
SystemVmTemplateRegistration::parseMetadataFile);
assertTrue(exception.getMessage().contains("Failed to parse systemVM template metadata file"));
}
}
@Test
public void test_parseMetadataFile_success() {
String metadataFileContent = "[default]\n" +
"version = x.y.z.0\n" +
"\n" +
"[kvm-x86_64]\n" +
"templatename = systemvm-kvm-x.y.z\n" +
"checksum = abc1\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"\n" +
"[kvm-aarch64]\n" +
"templatename = systemvm-kvm-x.y.z\n" +
"checksum = abc2\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"filename = systemvmtemplate-x.y.z-kvm.qcow2.bz2\n" +
"\n" +
"[vmware]\n" +
"templatename = systemvm-vmware-x.y.z\n" +
"checksum = abc3\n" +
"downloadurl = https://download.cloudstack.org/systemvm/x.y/systemvmtemplate-x.y.z-vmware.ova\n" +
"filename = systemvmtemplate-x.y.z-vmware.ova\n";
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS)) {
setupMetadataFile(mockedStatic, metadataFileContent);
String version = SystemVmTemplateRegistration.parseMetadataFile();
assertEquals("x.y.z.0", version);
}
assertNull(SystemVmTemplateRegistration.NewTemplateMap.get("xenserver"));
SystemVmTemplateRegistration.MetadataTemplateDetails templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("kvm-x86_64");
assertNotNull(templateDetails);
assertEquals(CPU.CPUArch.amd64, templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType());
templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("kvm-aarch64");
assertNotNull(templateDetails);
assertEquals(CPU.CPUArch.arm64, templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.KVM, templateDetails.getHypervisorType());
templateDetails =
SystemVmTemplateRegistration.NewTemplateMap.get("vmware");
assertNotNull(templateDetails);
assertNull(templateDetails.getArch());
assertEquals(Hypervisor.HypervisorType.VMware, templateDetails.getHypervisorType());
}
@Test
public void testMountStore_nullStoreUrl() throws Exception {
try (MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
SystemVmTemplateRegistration.mountStore(null, "/mnt/nfs", "nfs3");
scriptMock.verifyNoInteractions();
}
}
@Test
public void testMountStore_validStoreUrl() throws Exception {
String storeUrl = "nfs://192.168.1.100/export";
String path = "/mnt/nfs";
String nfsVersion = "nfs3";
String expectedMountCommand = "expectedMountCommand";
try (MockedStatic<UriUtils> uriUtilsMock = Mockito.mockStatic(UriUtils.class);
MockedStatic<SystemVmTemplateRegistration> sysVmMock =
Mockito.mockStatic(SystemVmTemplateRegistration.class, Mockito.CALLS_REAL_METHODS);
MockedStatic<Script> scriptMock = Mockito.mockStatic(Script.class)) {
uriUtilsMock.when(() -> UriUtils.encodeURIComponent(storeUrl)).thenReturn(storeUrl);
sysVmMock.when(() -> SystemVmTemplateRegistration.getMountCommand(
eq(nfsVersion),
eq("192.168.1.100:/export"),
eq(path)
)).thenReturn(expectedMountCommand);
SystemVmTemplateRegistration.mountStore(storeUrl, path, nfsVersion);
scriptMock.verify(() -> Script.runSimpleBashScript(expectedMountCommand), times(1));
}
}
@Test
public void testValidateTemplateFile_fileNotFound() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos");
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details);
try {
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
fail("Expected CloudRuntimeException due to missing template file");
} catch (CloudRuntimeException e) {
assertEquals("Failed to find local template file", e.getMessage());
}
}
@Test
public void testValidateTemplateFile_checksumMismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos");
File dummyFile = new File("dummy.txt");
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile);
try {
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
fail("Expected CloudRuntimeException due to checksum failure");
} catch (CloudRuntimeException e) {
assertEquals("Checksum failed for local template file", e.getMessage());
}
}
@Test
public void testValidateTemplateFile_success() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file", "url", "checksum", CPU.CPUArch.amd64, "guestos");
File dummyFile = new File("dummy.txt");
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
details.getHypervisorType(), details.getArch()), details);
doReturn(dummyFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, dummyFile);
systemVmTemplateRegistration.validateTemplateFileForHypervisorAndArch(details.getHypervisorType(),
details.getArch());
}
@Test
public void testValidateAndRegisterTemplate() {
Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM;
String name = "TestTemplate";
Long storeId = 123L;
VMTemplateVO templateVO = new VMTemplateVO();
templateVO.setArch(CPU.CPUArch.x86);
TemplateDataStoreVO templateDataStoreVO = new TemplateDataStoreVO();
String filePath = "/dummy/path";
doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch());
doNothing().when(systemVmTemplateRegistration).registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath);
systemVmTemplateRegistration.validateAndRegisterTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath);
verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(templateVO.getArch()));
verify(systemVmTemplateRegistration).registerTemplate(eq(hypervisor), eq(name), eq(storeId), eq(templateVO), eq(templateDataStoreVO), eq(filePath));
}
@Test
public void testValidateAndRegisterTemplateForNonExistingEntries() {
Hypervisor.HypervisorType hypervisor = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.amd64;
String name = "TestTemplateNonExisting";
Pair<String, Long> storeUrlAndId = new Pair<>("nfs://dummy", 456L);
String filePath = "/dummy/path/nonexisting";
doNothing().when(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(hypervisor, arch);
doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath);
systemVmTemplateRegistration.validateAndRegisterTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath);
verify(systemVmTemplateRegistration).validateTemplateFileForHypervisorAndArch(eq(hypervisor), eq(arch));
verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(eq(hypervisor), eq(arch), eq(name), eq(storeUrlAndId), eq(filePath));
}
@Test
public void testGetTemplateFile_fileExists() throws Exception {
File tempFile = File.createTempFile("template", ".qcow2");
tempFile.deleteOnExit();
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getDefaultFilePath()).thenReturn(tempFile.getAbsolutePath());
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNotNull(result);
assertEquals(tempFile.getAbsolutePath(), result.getAbsolutePath());
}
@Test
public void testGetTemplateFile_fileDoesNotExist_downloadFails() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "nonexistent.qcow2", "http://example.com/file.qcow2",
"", CPU.CPUArch.arm64, "guestos");
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class);
MockedStatic<HttpUtils> httpMock = Mockito.mockStatic(HttpUtils.class)) {
filesMock.when(() -> Files.isWritable(any(Path.class))).thenReturn(true);
httpMock.when(() -> HttpUtils.downloadFileWithProgress(eq(details.getUrl()), anyString(), any()))
.thenReturn(false);
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNull(result);
}
}
@Test
public void testGetTemplateFile_fileDoesNotExist_downloadSucceeds() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
new SystemVmTemplateRegistration.MetadataTemplateDetails(Hypervisor.HypervisorType.KVM,
"name", "file.qcow2", "http://example.com/file.qcow2",
"", CPU.CPUArch.arm64, "guestos");
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class);
MockedStatic<HttpUtils> httpMock = Mockito.mockStatic(HttpUtils.class)) {
filesMock.when(() -> Files.isWritable(any(Path.class))).thenReturn(false);
File expectedFile = new File(systemVmTemplateRegistration.getTempDownloadDir(), details.getFilename());
httpMock.when(() -> HttpUtils.downloadFileWithProgress(eq(details.getUrl()), eq(expectedFile.getAbsolutePath()), any()))
.thenReturn(true);
File result = systemVmTemplateRegistration.getTemplateFile(details);
assertNotNull(result);
assertEquals(expectedFile.getAbsolutePath(), result.getAbsolutePath());
assertEquals(expectedFile.getAbsolutePath(), details.getDownloadedFilePath());
}
}
@Test
public void testIsTemplateFileChecksumDifferent_noMismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getChecksum()).thenReturn("dummyChecksum");
File file = new File("dummy.txt");
try (MockedStatic<DigestHelper> digestMock = Mockito.mockStatic(DigestHelper.class)) {
digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("dummyChecksum");
boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file);
assertFalse(result);
}
}
@Test
public void testIsTemplateFileChecksumDifferent_mismatch() {
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
when(details.getChecksum()).thenReturn("expectedChecksum");
File file = new File("dummy.txt");
try (MockedStatic<DigestHelper> digestMock = Mockito.mockStatic(DigestHelper.class)) {
digestMock.when(() -> DigestHelper.calculateChecksum(file)).thenReturn("actualChecksum");
boolean result = systemVmTemplateRegistration.isTemplateFileChecksumDifferent(details, file);
assertTrue(result);
}
}
@Test(expected = CloudRuntimeException.class)
public void testValidateTemplates_metadataTemplateFailure() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
systemVmTemplateRegistration.validateTemplates(list);
}
@Test(expected = CloudRuntimeException.class)
public void testValidateTemplates_fileFailure() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details);
File mockFile = Mockito.mock(File.class);
doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(true).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile);
systemVmTemplateRegistration.validateTemplates(list);
}
public void testValidateTemplates_downloadableFileNotFound() {
CPU.CPUArch arch = SystemVmTemplateRegistration.DOWNLOADABLE_TEMPLATE_ARCH_TYPES.get(0);
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, arch));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, arch), details);
doReturn(null).when(systemVmTemplateRegistration).getTemplateFile(details);
systemVmTemplateRegistration.validateTemplates(list);
}
@Test
public void testValidateTemplates_success() {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> list = new ArrayList<>();
list.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64));
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
SystemVmTemplateRegistration.NewTemplateMap.put(SystemVmTemplateRegistration.getHypervisorArchKey(
Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), details);
File mockFile = Mockito.mock(File.class);
doReturn(mockFile).when(systemVmTemplateRegistration).getTemplateFile(details);
doReturn(false).when(systemVmTemplateRegistration).isTemplateFileChecksumDifferent(details, mockFile);
systemVmTemplateRegistration.validateTemplates(list);
}
@Test
public void testRegisterTemplatesForZone() {
long zoneId = 1L;
String filePath = "dummyFilePath";
String nfsVersion = "nfs3";
Pair<String, Long> storeUrlAndId = new Pair<>("nfs://dummy", 100L);
doReturn(storeUrlAndId).when(systemVmTemplateRegistration).getNfsStoreInZone(zoneId);
doReturn(nfsVersion).when(systemVmTemplateRegistration).getNfsVersion(storeUrlAndId.second());
try (MockedStatic<SystemVmTemplateRegistration> mockedStatic = Mockito.mockStatic(
SystemVmTemplateRegistration.class)) {
List<Pair<Hypervisor.HypervisorType, CPU.CPUArch>> hypervisorArchList = new ArrayList<>();
Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM;
CPU.CPUArch arch = CPU.CPUArch.getDefault();
hypervisorArchList.add(new Pair<>(hypervisorType, arch));
doReturn(hypervisorArchList).when(clusterDao).listDistinctHypervisorsAndArchExcludingExternalType(zoneId);
SystemVmTemplateRegistration.MetadataTemplateDetails details =
Mockito.mock(SystemVmTemplateRegistration.MetadataTemplateDetails.class);
String name = "existing";
Mockito.when(details.getArch()).thenReturn(CPU.CPUArch.getDefault());
Mockito.when(details.getName()).thenReturn(name);
mockedStatic.when(() -> SystemVmTemplateRegistration.getMetadataTemplateDetails(Mockito.any(),
Mockito.any())).thenReturn(details);
when(systemVmTemplateRegistration.getRegisteredTemplate(name, arch))
.thenReturn(null);
doNothing().when(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(
hypervisorType, arch,
name, storeUrlAndId, filePath);
systemVmTemplateRegistration.registerTemplatesForZone(zoneId, filePath);
mockedStatic.verify(() -> SystemVmTemplateRegistration.mountStore(storeUrlAndId.first(), filePath,
nfsVersion));
verify(systemVmTemplateRegistration).registerTemplateForNonExistingEntries(hypervisorType,
arch, name, storeUrlAndId, filePath);
}
}
}