| #!/usr/bin/python3 |
| # 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. |
| |
| import os |
| import sys |
| # ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ---- |
| # ---- We do this so cloud_utils can be looked up in the following order: |
| # ---- 1) Sources directory |
| # ---- 2) waf configured PYTHONDIR |
| # ---- 3) System Python path |
| for pythonpath in ( |
| "@PYTHONDIR@", |
| os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"), |
| ): |
| if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath) |
| # ---- End snippet of code ---- |
| |
| from cloudutils.syscfg import sysConfigFactory |
| from cloudutils.utilities import initLoging, UnknownSystemException |
| from cloudutils.cloudException import CloudRuntimeException, CloudInternalException |
| from cloudutils.globalEnv import globalEnv |
| from cloudutils.serviceConfigServer import cloudManagementConfig |
| from optparse import OptionParser |
| import urllib.request |
| import configparser |
| import hashlib |
| |
| SYSTEMVM_TEMPLATES_PATH = "/usr/share/cloudstack-management/templates/systemvm" |
| SYSTEMVM_TEMPLATES_METADATA_FILE = SYSTEMVM_TEMPLATES_PATH + "/metadata.ini" |
| |
| def verify_sha512_checksum(file_path, expected_checksum): |
| sha512 = hashlib.sha512() |
| try: |
| with open(file_path, "rb") as f: |
| for chunk in iter(lambda: f.read(8192), b""): |
| sha512.update(chunk) |
| return sha512.hexdigest().lower() == expected_checksum.lower() |
| except Exception as e: |
| print(f"Failed to verify checksum for {file_path}: {e}") |
| return False |
| |
| def download_file(url, dest_path, chunk_size=8 * 1024 * 1024): |
| """ |
| Downloads a file from the given URL to the specified destination path in chunks. |
| """ |
| try: |
| with urllib.request.urlopen(url) as response: |
| total_size = response.length if response.length else None |
| downloaded = 0 |
| try: |
| with open(dest_path, 'wb') as out_file: |
| while True: |
| chunk = response.read(chunk_size) |
| if not chunk: |
| break |
| out_file.write(chunk) |
| downloaded += len(chunk) |
| if total_size: |
| print(f"Downloaded {downloaded / (1024 * 1024):.2f}MB of {total_size / (1024 * 1024):.2f}MB", end='\r') |
| except PermissionError as pe: |
| print(f"Permission denied: {dest_path}") |
| raise |
| print(f"\nDownloaded file from {url} to {dest_path}") |
| except Exception as e: |
| print(f"Failed to download file: {e}") |
| raise |
| |
| def download_template_if_needed(template, url, filename, checksum): |
| dest_path = os.path.join(SYSTEMVM_TEMPLATES_PATH, filename) |
| if os.path.exists(dest_path): |
| if checksum and verify_sha512_checksum(dest_path, checksum): |
| print(f"{template} System VM template already exists at {dest_path} with valid checksum, skipping download.") |
| return |
| else: |
| print(f"{template} System VM template at {dest_path} has invalid or missing checksum, re-downloading...") |
| else: |
| print(f"Downloading {template} System VM template from {url} to {dest_path}...") |
| try: |
| download_file(url, dest_path) |
| #After download, verify checksum if provided |
| if checksum: |
| if verify_sha512_checksum(dest_path, checksum): |
| print(f"{template} System VM template downloaded and verified successfully.") |
| else: |
| print(f"ERROR: Checksum verification failed for {template} System VM template after download.") |
| except Exception as e: |
| print(f"ERROR: Failed to download {template} System VM template: {e}") |
| |
| def collect_template_metadata(selected_templates, options): |
| template_metadata_list = [] |
| if not os.path.exists(SYSTEMVM_TEMPLATES_METADATA_FILE): |
| print(f"ERROR: System VM templates metadata file not found at {SYSTEMVM_TEMPLATES_METADATA_FILE}, cannot download templates.") |
| sys.exit(1) |
| config = configparser.ConfigParser() |
| config.read(SYSTEMVM_TEMPLATES_METADATA_FILE) |
| template_repo_url = None |
| if options.systemvm_templates_repository: |
| if "default" in config and "downloadrepository" in config["default"]: |
| template_repo_url = config["default"]["downloadrepository"].strip() |
| if not template_repo_url: |
| print("ERROR: downloadrepository value is empty in metadata file, cannot use --systemvm-template-repository option.") |
| sys.exit(1) |
| for template in selected_templates: |
| if template in config: |
| url = config[template].get("downloadurl") |
| filename = config[template].get("filename") |
| checksum = config[template].get("checksum") |
| if url and filename: |
| if template_repo_url: |
| url = url.replace(template_repo_url, options.systemvm_templates_repository) |
| template_metadata_list.append({ |
| "template": template, |
| "url": url, |
| "filename": filename, |
| "checksum": checksum |
| }) |
| else: |
| print(f"ERROR: URL or filename not found for {template} System VM template in metadata.") |
| sys.exit(1) |
| else: |
| print(f"ERROR: No metadata found for {template} System VM template.") |
| sys.exit(1) |
| return template_metadata_list |
| |
| if __name__ == '__main__': |
| initLoging("@MSLOGDIR@/setupManagement.log") |
| glbEnv = globalEnv() |
| |
| parser = OptionParser() |
| parser.add_option("--https", action="store_true", dest="https", help="Enable HTTPs connection of management server") |
| parser.add_option("--tomcat7", action="store_true", dest="tomcat7", help="Depreciated option, don't use it") |
| parser.add_option("--no-start", action="store_true", dest="nostart", help="Do not start management server after successful configuration") |
| parser.add_option( |
| "--systemvm-templates", |
| dest="systemvm_templates", |
| 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.", |
| ) |
| parser.add_option( |
| "--systemvm-templates-repository", |
| dest="systemvm_templates_repository", |
| help="Specify the URL to download System VM templates from." |
| ) |
| (options, args) = parser.parse_args() |
| if options.https: |
| glbEnv.svrMode = "HttpsServer" |
| if options.tomcat7: |
| print("The --tomcat7 option is deprecated, CloudStack now uses embedded Jetty server.") |
| if options.nostart: |
| glbEnv.noStart = True |
| |
| available_templates = ["kvm-aarch64", "kvm-x86_64", "xenserver", "vmware"] |
| templates_arg = options.systemvm_templates |
| |
| selected_templates = ["kvm-x86_64"] |
| if templates_arg: |
| templates_list = [t.strip().lower() for t in templates_arg.split(",")] |
| if "all" in templates_list: |
| if len(templates_list) > 1: |
| print("WARNING: 'all' specified for System VM templates, ignoring other specified templates.") |
| selected_templates = available_templates |
| else: |
| invalid_templates = [] |
| for t in templates_list: |
| if t in available_templates: |
| if t not in selected_templates: |
| selected_templates.append(t) |
| else: |
| if t not in invalid_templates: |
| invalid_templates.append(t) |
| if invalid_templates: |
| print(f"ERROR: Invalid System VM template names provided: {', '.join(invalid_templates)}") |
| sys.exit(1) |
| print(f"Selected systemvm templates to download: {', '.join(selected_templates) if selected_templates else 'None'}") |
| |
| template_metadata_list = [] |
| if selected_templates: |
| template_metadata_list = collect_template_metadata(selected_templates, options) |
| |
| glbEnv.mode = "Server" |
| |
| print("Starting to configure CloudStack Management Server:") |
| try: |
| syscfg = sysConfigFactory.getSysConfigFactory(glbEnv) |
| except UnknownSystemException: |
| print("Error: CloudStack failed to detect your " |
| "operating system. Exiting.", file=sys.stderr) |
| sys.exit(1) |
| try: |
| syscfg.registerService(cloudManagementConfig) |
| syscfg.config() |
| print("CloudStack Management Server setup is Done!") |
| print("Please ensure ports 8080, 8250, 8443, and 9090 are opened and not firewalled for the management server and not in use by other processes on this host.") |
| except (CloudRuntimeException, CloudInternalException) as e: |
| print(e) |
| print("Try to restore your system:") |
| try: |
| syscfg.restore() |
| except: |
| pass |
| |
| for meta in template_metadata_list: |
| download_template_if_needed(meta["template"], meta["url"], meta["filename"], meta["checksum"]) |