Skip to content

Commit de925b1

Browse files
committed
Merge remote-tracking branch 'apache/main' into feature-ui-logs
2 parents d717b65 + 40d21f0 commit de925b1

File tree

12 files changed

+2234
-536
lines changed

12 files changed

+2234
-536
lines changed

api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.apache.cloudstack.api.BaseCmd;
2727
import org.apache.cloudstack.api.Parameter;
2828
import org.apache.cloudstack.api.ServerApiException;
29-
import org.apache.cloudstack.api.response.BackupResponse;
3029
import org.apache.cloudstack.api.response.BackupScheduleResponse;
3130
import org.apache.cloudstack.api.response.UserVmResponse;
3231
import org.apache.cloudstack.backup.BackupManager;
@@ -38,7 +37,7 @@
3837

3938
@APICommand(name = "createBackupSchedule",
4039
description = "Creates a User-defined Instance backup schedule",
41-
responseObject = BackupResponse.class, since = "4.14.0",
40+
responseObject = BackupScheduleResponse.class, since = "4.14.0",
4241
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User})
4342
public class CreateBackupScheduleCmd extends BaseCmd {
4443

client/bindir/cloud-setup-management.in

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,106 @@ from cloudutils.cloudException import CloudRuntimeException, CloudInternalExcept
3636
from cloudutils.globalEnv import globalEnv
3737
from cloudutils.serviceConfigServer import cloudManagementConfig
3838
from optparse import OptionParser
39+
import urllib.request
40+
import configparser
41+
import hashlib
42+
43+
SYSTEMVM_TEMPLATES_PATH = "/usr/share/cloudstack-management/templates/systemvm"
44+
SYSTEMVM_TEMPLATES_METADATA_FILE = SYSTEMVM_TEMPLATES_PATH + "/metadata.ini"
45+
46+
def verify_sha512_checksum(file_path, expected_checksum):
47+
sha512 = hashlib.sha512()
48+
try:
49+
with open(file_path, "rb") as f:
50+
for chunk in iter(lambda: f.read(8192), b""):
51+
sha512.update(chunk)
52+
return sha512.hexdigest().lower() == expected_checksum.lower()
53+
except Exception as e:
54+
print(f"Failed to verify checksum for {file_path}: {e}")
55+
return False
56+
57+
def download_file(url, dest_path, chunk_size=8 * 1024 * 1024):
58+
"""
59+
Downloads a file from the given URL to the specified destination path in chunks.
60+
"""
61+
try:
62+
with urllib.request.urlopen(url) as response:
63+
total_size = response.length if response.length else None
64+
downloaded = 0
65+
try:
66+
with open(dest_path, 'wb') as out_file:
67+
while True:
68+
chunk = response.read(chunk_size)
69+
if not chunk:
70+
break
71+
out_file.write(chunk)
72+
downloaded += len(chunk)
73+
if total_size:
74+
print(f"Downloaded {downloaded / (1024 * 1024):.2f}MB of {total_size / (1024 * 1024):.2f}MB", end='\r')
75+
except PermissionError as pe:
76+
print(f"Permission denied: {dest_path}")
77+
raise
78+
print(f"\nDownloaded file from {url} to {dest_path}")
79+
except Exception as e:
80+
print(f"Failed to download file: {e}")
81+
raise
82+
83+
def download_template_if_needed(template, url, filename, checksum):
84+
dest_path = os.path.join(SYSTEMVM_TEMPLATES_PATH, filename)
85+
if os.path.exists(dest_path):
86+
if checksum and verify_sha512_checksum(dest_path, checksum):
87+
print(f"{template} System VM template already exists at {dest_path} with valid checksum, skipping download.")
88+
return
89+
else:
90+
print(f"{template} System VM template at {dest_path} has invalid or missing checksum, re-downloading...")
91+
else:
92+
print(f"Downloading {template} System VM template from {url} to {dest_path}...")
93+
try:
94+
download_file(url, dest_path)
95+
#After download, verify checksum if provided
96+
if checksum:
97+
if verify_sha512_checksum(dest_path, checksum):
98+
print(f"{template} System VM template downloaded and verified successfully.")
99+
else:
100+
print(f"ERROR: Checksum verification failed for {template} System VM template after download.")
101+
except Exception as e:
102+
print(f"ERROR: Failed to download {template} System VM template: {e}")
103+
104+
def collect_template_metadata(selected_templates, options):
105+
template_metadata_list = []
106+
if not os.path.exists(SYSTEMVM_TEMPLATES_METADATA_FILE):
107+
print(f"ERROR: System VM templates metadata file not found at {SYSTEMVM_TEMPLATES_METADATA_FILE}, cannot download templates.")
108+
sys.exit(1)
109+
config = configparser.ConfigParser()
110+
config.read(SYSTEMVM_TEMPLATES_METADATA_FILE)
111+
template_repo_url = None
112+
if options.systemvm_templates_repository:
113+
if "default" in config and "downloadrepository" in config["default"]:
114+
template_repo_url = config["default"]["downloadrepository"].strip()
115+
if not template_repo_url:
116+
print("ERROR: downloadrepository value is empty in metadata file, cannot use --systemvm-template-repository option.")
117+
sys.exit(1)
118+
for template in selected_templates:
119+
if template in config:
120+
url = config[template].get("downloadurl")
121+
filename = config[template].get("filename")
122+
checksum = config[template].get("checksum")
123+
if url and filename:
124+
if template_repo_url:
125+
url = url.replace(template_repo_url, options.systemvm_templates_repository)
126+
template_metadata_list.append({
127+
"template": template,
128+
"url": url,
129+
"filename": filename,
130+
"checksum": checksum
131+
})
132+
else:
133+
print(f"ERROR: URL or filename not found for {template} System VM template in metadata.")
134+
sys.exit(1)
135+
else:
136+
print(f"ERROR: No metadata found for {template} System VM template.")
137+
sys.exit(1)
138+
return template_metadata_list
39139

40140
if __name__ == '__main__':
41141
initLoging("@MSLOGDIR@/setupManagement.log")
@@ -45,6 +145,16 @@ if __name__ == '__main__':
45145
parser.add_option("--https", action="store_true", dest="https", help="Enable HTTPs connection of management server")
46146
parser.add_option("--tomcat7", action="store_true", dest="tomcat7", help="Depreciated option, don't use it")
47147
parser.add_option("--no-start", action="store_true", dest="nostart", help="Do not start management server after successful configuration")
148+
parser.add_option(
149+
"--systemvm-templates",
150+
dest="systemvm_templates",
151+
help="Specify System VM templates to download: all, kvm-aarch64, kvm-x86_64, xenserver, vmware or comma-separated list of hypervisor combinations (e.g., kvm-x86_64,xenserver). Default is kvm-x86_64.",
152+
)
153+
parser.add_option(
154+
"--systemvm-templates-repository",
155+
dest="systemvm_templates_repository",
156+
help="Specify the URL to download System VM templates from."
157+
)
48158
(options, args) = parser.parse_args()
49159
if options.https:
50160
glbEnv.svrMode = "HttpsServer"
@@ -53,6 +163,34 @@ if __name__ == '__main__':
53163
if options.nostart:
54164
glbEnv.noStart = True
55165

166+
available_templates = ["kvm-aarch64", "kvm-x86_64", "xenserver", "vmware"]
167+
templates_arg = options.systemvm_templates
168+
169+
selected_templates = ["kvm-x86_64"]
170+
if templates_arg:
171+
templates_list = [t.strip().lower() for t in templates_arg.split(",")]
172+
if "all" in templates_list:
173+
if len(templates_list) > 1:
174+
print("WARNING: 'all' specified for System VM templates, ignoring other specified templates.")
175+
selected_templates = available_templates
176+
else:
177+
invalid_templates = []
178+
for t in templates_list:
179+
if t in available_templates:
180+
if t not in selected_templates:
181+
selected_templates.append(t)
182+
else:
183+
if t not in invalid_templates:
184+
invalid_templates.append(t)
185+
if invalid_templates:
186+
print(f"ERROR: Invalid System VM template names provided: {', '.join(invalid_templates)}")
187+
sys.exit(1)
188+
print(f"Selected systemvm templates to download: {', '.join(selected_templates) if selected_templates else 'None'}")
189+
190+
template_metadata_list = []
191+
if selected_templates:
192+
template_metadata_list = collect_template_metadata(selected_templates, options)
193+
56194
glbEnv.mode = "Server"
57195

58196
print("Starting to configure CloudStack Management Server:")
@@ -74,3 +212,6 @@ if __name__ == '__main__':
74212
syscfg.restore()
75213
except:
76214
pass
215+
216+
for meta in template_metadata_list:
217+
download_template_if_needed(meta["template"], meta["url"], meta["filename"], meta["checksum"])

client/conf/server.properties.in

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ extensions.deployment.mode=@EXTENSIONSDEPLOYMENTMODE@
6363
#threads.min=10
6464
#threads.max=500
6565

66+
# The URL prefix for the system VM templates repository. When downloading system VM templates, the server replaces the
67+
# `downloadrepository` key value from the metadata file in template URLs. If not specified, the original template URL
68+
# will be used for download.
69+
# system.vm.templates.download.repository=http://download.cloudstack.org/systemvm/
70+
6671
# WebSocket server enable/disable flag used by the management server
6772
websocket.enable=true
6873

engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDetailsDaoImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ public class DataCenterDetailsDaoImpl extends ResourceDetailsDaoBase<DataCenterD
3131

3232
private final SearchBuilder<DataCenterDetailVO> DetailSearch;
3333

34-
DataCenterDetailsDaoImpl() {
34+
public DataCenterDetailsDaoImpl() {
35+
super();
3536
DetailSearch = createSearchBuilder();
3637
DetailSearch.and("zoneId", DetailSearch.entity().getResourceId(), SearchCriteria.Op.EQ);
3738
DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ);

engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
9494

9595
List<VMTemplateVO> listByParentTemplatetId(long parentTemplatetId);
9696

97-
VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch);
97+
VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch);
9898

9999
List<VMTemplateVO> findTemplatesLinkedToUserdata(long userdataId);
100100

@@ -103,4 +103,7 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long>, StateDao<
103103
List<Long> listIdsByTemplateTag(String tag);
104104

105105
List<Long> listIdsByExtensionId(long extensionId);
106+
107+
VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType,
108+
CPU.CPUArch arch, String urlPathSuffix);
106109
}

engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,17 @@ public List<VMTemplateVO> listReadyTemplates() {
245245

246246

247247
@Override
248-
public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) {
248+
public VMTemplateVO findLatestTemplateByName(String name, HypervisorType hypervisorType, CPU.CPUArch arch) {
249249
SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
250250
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
251+
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
251252
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
252253
sb.done();
253254
SearchCriteria<VMTemplateVO> sc = sb.create();
254255
sc.setParameters("name", name);
256+
if (hypervisorType != null) {
257+
sc.setParameters("hypervisorType", hypervisorType);
258+
}
255259
if (arch != null) {
256260
sc.setParameters("arch", arch);
257261
}
@@ -850,6 +854,37 @@ public List<Long> listIdsByExtensionId(long extensionId) {
850854
return customSearch(sc, null);
851855
}
852856

857+
@Override
858+
public VMTemplateVO findActiveSystemTemplateByHypervisorArchAndUrlPath(HypervisorType hypervisorType,
859+
CPU.CPUArch arch, String urlPathSuffix) {
860+
if (StringUtils.isBlank(urlPathSuffix)) {
861+
return null;
862+
}
863+
SearchBuilder<VMTemplateVO> sb = createSearchBuilder();
864+
sb.and("templateType", sb.entity().getTemplateType(), SearchCriteria.Op.EQ);
865+
sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ);
866+
sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ);
867+
sb.and("urlPathSuffix", sb.entity().getUrl(), SearchCriteria.Op.LIKE);
868+
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
869+
sb.done();
870+
SearchCriteria<VMTemplateVO> sc = sb.create();
871+
sc.setParameters("templateType", TemplateType.SYSTEM);
872+
if (hypervisorType != null) {
873+
sc.setParameters("hypervisorType", hypervisorType);
874+
}
875+
if (arch != null) {
876+
sc.setParameters("arch", arch);
877+
}
878+
sc.setParameters("urlPathSuffix", "%" + urlPathSuffix);
879+
sc.setParameters("state", VirtualMachineTemplate.State.Active);
880+
Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L);
881+
List<VMTemplateVO> templates = listBy(sc, filter);
882+
if (CollectionUtils.isNotEmpty(templates)) {
883+
return templates.get(0);
884+
}
885+
return null;
886+
}
887+
853888
@Override
854889
public boolean updateState(
855890
com.cloud.template.VirtualMachineTemplate.State currentState,

0 commit comments

Comments
 (0)