diff --git a/.asf.yaml b/.asf.yaml index 43f0351bc7c..be818fda4d3 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -53,11 +53,12 @@ github: - acs-robot - gpordeus - hsato03 - - bernardodemarco - - abh1sar - FelipeM525 - lucas-a-martins - nicoschmdt + - abh1sar + - rosi-shapeblue + - sudo87 protected_branches: ~ diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index 5e8d65905eb..531c26ace44 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -86,3 +86,6 @@ MD046: false # MD052/reference-links-images Reference links and images should use a label that is defined MD052: false + +# MD059/descriptive-link-text Link text should be descriptive +MD059: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd3c8f8ac67..06f9f424c7f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,13 +32,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: '11' - distribution: 'adopt' - architecture: x64 - cache: maven + distribution: 'temurin' + java-version: '17' + cache: 'maven' - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ceffb42c79b..471e56e3f07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,10 @@ jobs: smoke/test_nested_virtualization smoke/test_set_sourcenat smoke/test_webhook_lifecycle - smoke/test_purge_expunged_vms", + smoke/test_purge_expunged_vms + smoke/test_extension_lifecycle + smoke/test_extension_custom_action_lifecycle + smoke/test_extension_custom", "smoke/test_network smoke/test_network_acl smoke/test_network_ipv6 @@ -137,6 +140,7 @@ jobs: smoke/test_vm_deployment_planner smoke/test_vm_strict_host_tags smoke/test_vm_schedule + smoke/test_deploy_vgpu_enabled_vm smoke/test_vm_life_cycle smoke/test_vm_lifecycle_unmanage_import smoke/test_vm_snapshot_kvm @@ -164,7 +168,8 @@ jobs: component/test_cpu_limits component/test_cpu_max_limits component/test_cpu_project_limits - component/test_deploy_vm_userdata_multi_nic", + component/test_deploy_vm_userdata_multi_nic + component/test_deploy_vm_lease", "component/test_egress_fw_rules component/test_invalid_gw_nm component/test_ip_reservation", @@ -215,13 +220,12 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: '11' - distribution: 'adopt' - architecture: x64 - cache: maven + distribution: 'temurin' + java-version: '17' + cache: 'maven' - name: Set up Python uses: actions/setup-python@v5 @@ -236,7 +240,7 @@ jobs: - name: Install Python dependencies run: | - python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid + python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycryptodome mock flask netaddr pylint pycodestyle six astroid pynose - name: Install jacoco dependencies run: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..85f348b35bf --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,48 @@ +# 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. + +name: CodeQL Analysis +on: + push: + branches: [main] + pull_request: + branches: [main] +permissions: + actions: read + contents: read + security-events: write +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: ["actions"] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "Security" diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index b6c814a36f4..0bc87c6cc13 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -name: Lint +name: pre-commit on: [pull_request] diff --git a/.github/workflows/main-sonar-check.yml b/.github/workflows/main-sonar-check.yml index 8248e48022a..dccd7174e54 100644 --- a/.github/workflows/main-sonar-check.yml +++ b/.github/workflows/main-sonar-check.yml @@ -54,7 +54,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/sonar-check.yml b/.github/workflows/sonar-check.yml index c36bceb2b90..d31f55980a8 100644 --- a/.github/workflows/sonar-check.yml +++ b/.github/workflows/sonar-check.yml @@ -56,7 +56,7 @@ jobs: uses: actions/cache@v4 with: path: ~/.m2/repository - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + key: ${{ runner.os }}-m2-${{ hashFiles('pom.xml', '*/pom.xml', '*/*/pom.xml', '*/*/*/pom.xml') }} restore-keys: | ${{ runner.os }}-m2 diff --git a/.github/workflows/ui.yml b/.github/workflows/ui.yml index 56f757133b7..88d4a70e4c2 100644 --- a/.github/workflows/ui.yml +++ b/.github/workflows/ui.yml @@ -56,6 +56,7 @@ jobs: npm run test:unit - uses: codecov/codecov-action@v4 + if: github.repository == 'apache/cloudstack' with: working-directory: ui files: ./coverage/lcov.info diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9bc87454f1..46db709c3fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,23 +15,31 @@ # specific language governing permissions and limitations # under the License. --- -default_stages: [commit, push] +default_stages: [pre-commit, pre-push] default_language_version: # force all unspecified Python hooks to run python3 python: python3 -minimum_pre_commit_version: "2.17.0" +minimum_pre_commit_version: "3.2.0" repos: - repo: meta hooks: - id: identity - id: check-hooks-apply + - repo: https://github.com/gitleaks/gitleaks + rev: v8.27.2 + hooks: + - id: gitleaks + name: run gitleaks + description: detect hardcoded secrets - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: #- id: check-added-large-files - id: check-case-conflict #- id: check-executables-have-shebangs - id: check-merge-conflict + - id: check-shebang-scripts-are-executable + files: \.sh$ - id: check-symlinks - id: check-vcs-permalinks #- id: check-yaml @@ -42,6 +50,7 @@ repos: exclude: > (?x) ^scripts/vm/systemvm/id_rsa\.cloud$| + ^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java$| ^server/src/test/java/com/cloud/keystore/KeystoreTest\.java$| ^server/src/test/resources/certs/dsa_self_signed\.key$| ^server/src/test/resources/certs/non_root\.key$| @@ -51,7 +60,8 @@ repos: ^server/src/test/resources/certs/rsa_self_signed\.key$| ^services/console-proxy/rdpconsole/src/test/doc/rdp-key\.pem$| ^systemvm/agent/certs/localhost\.key$| - ^systemvm/agent/certs/realhostip\.key$ + ^systemvm/agent/certs/realhostip\.key$| + ^test/integration/smoke/test_ssl_offloading.py$ - id: end-of-file-fixer exclude: \.vhd$ - id: fix-byte-order-marker @@ -59,7 +69,7 @@ repos: - id: mixed-line-ending exclude: \.cs$ - id: trailing-whitespace - files: \.(bat|cfg|cs|css|gitignore|header|in|install|java|md|properties|py|rb|sh|sql|txt|vue|xml|xsl|yaml|yml)$ + files: \.(bat|cfg|cs|css|gitignore|header|in|install|java|md|properties|py|rb|rc|sh|sql|te|template|txt|ucls|vue|xml|xsl|yaml|yml)$|^cloud-cli/bindir/cloud-tool$|^debian/changelog$ args: [--markdown-linebreak-ext=md] exclude: ^services/console-proxy/rdpconsole/src/test/doc/freerdp-debug-log\.txt$ - repo: https://github.com/codespell-project/codespell @@ -69,25 +79,14 @@ repos: name: run codespell description: Check spelling with codespell args: [--ignore-words=.github/linters/codespell.txt] - exclude: ^ui/package\.json$|^ui/package-lock\.json$|^ui/public/js/less\.min\.js$|^ui/public/locales/.*[^n].*\.json$ + exclude: ^systemvm/agent/noVNC/|^ui/package\.json$|^ui/package-lock\.json$|^ui/public/js/less\.min\.js$|^ui/public/locales/.*[^n].*\.json$|^server/src/test/java/org/apache/cloudstack/network/ssl/CertServiceTest.java$|^test/integration/smoke/test_ssl_offloading.py$ - repo: https://github.com/pycqa/flake8 rev: 7.0.0 hooks: - id: flake8 args: [--config, .github/linters/.flake8] - exclude: > - (?x) - ^agent/bindir/cloud-setup-agent\.in$| - ^client/bindir/cloud-update-xenserver-licenses\.in$| - ^cloud-cli/bindir/cloud-tool$| - ^python/bindir/cloud-grab-dependent-library-versions$| - ^python/bindir/cloud-setup-baremetal$| - ^scripts/vm/hypervisor/xenserver/storagePlugin$| - ^scripts/vm/hypervisor/xenserver/vmopspremium$| - ^setup/bindir/cloud-setup-encryption\.in$| - ^venv/.*$ - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.43.0 + rev: v0.45.0 hooks: - id: markdownlint name: run markdownlint diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a6d006938f3..17ff4badc97 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,9 +4,9 @@ Contributing to Apache CloudStack (ACS) Summary ------- This document covers how to contribute to the ACS project. ACS uses GitHub PRs to manage code contributions. -These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the ACS project and you will submit a Pull Request for your changes to be added. +These instructions assume you have a GitHub.com account, so if you don't have one you will have to create one. Your proposed code changes will be published to your own fork of the ACS project, and you will submit a Pull Request for your changes to be added. -_Lets get started!!!_ +_Let's get started!!!_ Bug fixes --------- @@ -26,7 +26,7 @@ No back porting / cherry-picking features to existing branches! PendingReleaseNotes file ------------------------ -When developing a new feature or making a (major) change to a existing feature you are encouraged to append this to the PendingReleaseNotes file so that the Release Manager can +When developing a new feature or making a (major) change to an existing feature you are encouraged to append this to the PendingReleaseNotes file so that the Release Manager can use this file as a source of information when compiling the Release Notes for a new release. When adding information to the PendingReleaseNotes file make sure that you write a good and understandable description of the new feature or change which you have developed. @@ -38,9 +38,9 @@ Fork the code In your browser, navigate to: [https://github.com/apache/cloudstack](https://github.com/apache/cloudstack) -Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command. +Fork the repository by clicking on the 'Fork' button on the top right hand side. The fork will happen, and you will be taken to your own fork of the repository. Copy the Git repository URL by clicking on the clipboard next to the URL on the right hand side of the page under '**HTTPS** clone URL'. You will paste this URL when doing the following `git clone` command. -On your computer, follow these steps to setup a local repository for working on ACS: +On your computer, follow these steps to set up a local repository for working on ACS: ```bash $ git clone https://github.com/YOUR_ACCOUNT/cloudstack.git @@ -92,9 +92,9 @@ $ git rebase main Make a GitHub Pull Request to contribute your changes ----------------------------------------------------- -When you are happy with your changes and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub. +When you are happy with your changes, and you are ready to contribute them, you will create a Pull Request on GitHub to do so. This is done by pushing your local changes to your forked repository (default remote name is `origin`) and then initiating a pull request on GitHub. -Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. Incase of UI PRs, a screenshot is preferred. +Please include JIRA id, detailed information about the bug/feature, what all tests are executed, how the reviewer can test this feature etc. In case of UI PRs, a screenshot is preferred. > **IMPORTANT:** Make sure you have rebased your `feature_x` branch to include the latest code from `upstream/main` _before_ you do this. diff --git a/INSTALL.md b/INSTALL.md index e133e7d7b91..efb42678b0a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -18,25 +18,26 @@ Install tools and dependencies used for development: # yum -y install git java-17-openjdk java-17-openjdk-devel \ mysql mysql-server mkisofs git gcc python MySQL-python openssh-clients wget -Set up Maven (3.6.0): +Set up Maven (3.9.10): - # wget http://www.us.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz - # tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local + # wget https://dlcdn.apache.org/maven/maven-3/3.9.10/binaries/apache-maven-3.9.10-bin.tar.gz + # sudo tar -zxvf apache-maven-3.9.10-bin.tar.gz -C /usr/local # cd /usr/local - # ln -s apache-maven-3.6.3 maven + # sudo ln -s apache-maven-3.9.10 maven # echo export M2_HOME=/usr/local/maven >> ~/.bashrc # or .zshrc or .profile # echo export PATH=/usr/local/maven/bin:${PATH} >> ~/.bashrc # or .zshrc or .profile # source ~/.bashrc -Setup up NodeJS (LTS): +Setup up Node.js 16: - # curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash - + # curl -sL https://rpm.nodesource.com/setup_16.x | sudo -E bash - # sudo yum install nodejs # sudo npm install -g @vue/cli npm-check-updates Start the MySQL service: $ service mysqld start + $ mysql_secure_installation ### Using jenv and/or pyenv for Version Management @@ -86,13 +87,33 @@ Start the management server: If this works, you've successfully setup a single server Apache CloudStack installation. -Open the following URL on your browser to access the Management Server UI: - - http://localhost:8080/client/ +To access the Management Server UI, follow the following procedure: The default credentials are; user: admin, password: password and the domain field should be left blank which is defaulted to the ROOT domain. +## To bring up CloudStack UI + +Move to UI Directory + + $ cd /path/to/cloudstack/ui + +To install dependencies. + + $ npm install + +To build the project. + + $ npm run build + +For Development Mode. + + $ npm start + +Make sure to set `CS_URL=http://localhost:8080` on the `.env.local` file on UI. + +You should be able to run the management server on http://localhost:5050 + ## Building with non-redistributable plugins CloudStack supports several plugins that depend on libraries with distribution restrictions. diff --git a/README.md b/README.md index f66a4dc6f97..2403b20d652 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,14 @@ -# Apache CloudStack [![Build Status](https://github.com/apache/cloudstack/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/build.yml) [![UI Build](https://github.com/apache/cloudstack/actions/workflows/ui.yml/badge.svg)](https://github.com/apache/cloudstack/actions/workflows/ui.yml) [![License Check](https://github.com/apache/cloudstack/actions/workflows/rat.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/rat.yml) [![Simulator CI](https://github.com/apache/cloudstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/ci.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=apache_cloudstack&metric=alert_status)](https://sonarcloud.io/dashboard?id=apache_cloudstack) [![codecov](https://codecov.io/gh/apache/cloudstack/branch/main/graph/badge.svg)](https://codecov.io/gh/apache/cloudstack) +# Apache CloudStack + +[![Build Status](https://github.com/apache/cloudstack/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/build.yml) +[![codecov](https://codecov.io/gh/apache/cloudstack/branch/main/graph/badge.svg)](https://codecov.io/gh/apache/cloudstack) +[![Docker CloudStack Simulator Status](https://github.com/apache/cloudstack/actions/workflows/docker-cloudstack-simulator.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/docker-cloudstack-simulator.yml) +[![License Check](https://github.com/apache/cloudstack/actions/workflows/rat.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/rat.yml) +[![Linter Status](https://github.com/apache/cloudstack/actions/workflows/linter.yml/badge.svg)](https://github.com/apache/cloudstack/actions/workflows/linter.yml) +[![Merge Conflict Checker Status](https://github.com/apache/cloudstack/actions/workflows/merge-conflict-checker.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/merge-conflict-checker.yml) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=apache_cloudstack&metric=alert_status)](https://sonarcloud.io/dashboard?id=apache_cloudstack) +[![Simulator CI](https://github.com/apache/cloudstack/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/ci.yml) +[![UI Build](https://github.com/apache/cloudstack/actions/workflows/ui.yml/badge.svg?branch=main)](https://github.com/apache/cloudstack/actions/workflows/ui.yml) [![Apache CloudStack](tools/logo/apache_cloudstack.png)](https://cloudstack.apache.org/) @@ -160,3 +170,11 @@ The following provides more details on the included cryptographic software: * CloudStack makes use of the Bouncy Castle general-purpose encryption library. * CloudStack can optionally interact with and control OpenSwan-based VPNs. * CloudStack has a dependency on and makes use of JSch - a java SSH2 implementation. + +## Star History + +[![Apache CloudStack Star History](https://api.star-history.com/svg?repos=apache/cloudstack&type=Date)](https://www.star-history.com/#apache/cloudstack&Date) + +## Contributors + +[![Apache CloudStack Contributors](https://contrib.rocks/image?repo=apache/cloudstack&anon=0&max=500)](https://github.com/apache/cloudstack/graphs/contributors) diff --git a/agent/bindir/cloud-setup-agent.in b/agent/bindir/cloud-setup-agent.in index 18de64089ed..dc4582540df 100755 --- a/agent/bindir/cloud-setup-agent.in +++ b/agent/bindir/cloud-setup-agent.in @@ -20,9 +20,22 @@ import os import logging import sys import socket + +# ---- 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.cloudException import CloudRuntimeException, CloudInternalException from cloudutils.utilities import initLoging, bash -from cloudutils.configFileOps import configFileOps +from cloudutils.configFileOps import configFileOps from cloudutils.globalEnv import globalEnv from cloudutils.networkConfig import networkConfig from cloudutils.syscfg import sysConfigFactory @@ -30,35 +43,41 @@ from cloudutils.serviceConfig import configureLibvirtConfig, configure_libvirt_t from optparse import OptionParser + def getUserInputs(): print("Welcome to the CloudStack Agent Setup:") cfo = configFileOps("@AGENTSYSCONFDIR@/agent.properties") oldMgt = cfo.getEntry("host") - mgtSvr = input("Please input the Management Server Hostname/IP-Address:[%s]"%oldMgt) + mgtSvr = input( + "Please input the Management Server Hostname/IP-Address:[%s]" % oldMgt + ) if mgtSvr == "": mgtSvr = oldMgt try: socket.getaddrinfo(mgtSvr, 443) except: - print("Failed to resolve %s. Please input a valid hostname or IP-Address."%mgtSvr) + print( + "Failed to resolve %s. Please input a valid hostname or IP-Address." + % mgtSvr + ) exit(1) oldToken = cfo.getEntry("zone") - zoneToken = input("Please input the Zone Id:[%s]"%oldToken) + zoneToken = input("Please input the Zone Id:[%s]" % oldToken) if zoneToken == "": zoneToken = oldToken oldPod = cfo.getEntry("pod") - podId = input("Please input the Pod Id:[%s]"%oldPod) + podId = input("Please input the Pod Id:[%s]" % oldPod) if podId == "": - podId = oldToken + podId = oldToken oldCluster = cfo.getEntry("cluster") - clusterId = input("Please input the Cluster Id:[%s]"%oldCluster) + clusterId = input("Please input the Cluster Id:[%s]" % oldCluster) if clusterId == "": clusterId = oldCluster @@ -66,18 +85,20 @@ def getUserInputs(): if oldHypervisor == "": oldHypervisor = "kvm" - hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]"%oldHypervisor) + hypervisor = input("Please input the Hypervisor type kvm/lxc:[%s]" % oldHypervisor) if hypervisor == "": hypervisor = oldHypervisor try: defaultNic = networkConfig.getDefaultNetwork() except: - print("Failed to get default route. Please configure your network to have a default route") + print( + "Failed to get default route. Please configure your network to have a default route" + ) exit(1) defNic = defaultNic.name - network = input("Please choose which network used to create VM:[%s]"%defNic) + network = input("Please choose which network used to create VM:[%s]" % defNic) if network == "": if defNic == "": print("You need to specify one of Nic or bridge on your system") @@ -87,7 +108,8 @@ def getUserInputs(): return [mgtSvr, zoneToken, network, podId, clusterId, hypervisor] -if __name__ == '__main__': + +if __name__ == "__main__": initLoging("@AGENTLOGDIR@/setup.log") glbEnv = globalEnv() @@ -95,13 +117,23 @@ if __name__ == '__main__': glbEnv.agentMode = "Agent" parser = OptionParser() parser.add_option("-a", action="store_true", dest="auto", help="auto mode") - parser.add_option("-m", "--host", dest="mgt", help="Management server hostname or IP-Address") + parser.add_option( + "-m", "--host", dest="mgt", help="Management server hostname or IP-Address" + ) parser.add_option("-z", "--zone", dest="zone", help="zone id") parser.add_option("-p", "--pod", dest="pod", help="pod id") parser.add_option("-c", "--cluster", dest="cluster", help="cluster id") - parser.add_option("-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type") + parser.add_option( + "-t", "--hypervisor", default="kvm", dest="hypervisor", help="hypervisor type" + ) parser.add_option("-g", "--guid", dest="guid", help="guid") - parser.add_option("-s", action="store_true", default=False, dest="secure", help="Secure and enable TLS for libvirtd") + parser.add_option( + "-s", + action="store_true", + default=False, + dest="secure", + help="Secure and enable TLS for libvirtd", + ) parser.add_option("--pubNic", dest="pubNic", help="Public traffic interface") parser.add_option("--prvNic", dest="prvNic", help="Private traffic interface") parser.add_option("--guestNic", dest="guestNic", help="Guest traffic interface") @@ -127,15 +159,15 @@ if __name__ == '__main__': glbEnv.pod = userInputs[3] glbEnv.cluster = userInputs[4] glbEnv.hypervisor = userInputs[5] - #generate UUID + # generate UUID glbEnv.uuid = old_config.getEntry("guid") if glbEnv.uuid == "": glbEnv.uuid = bash("uuidgen").getStdout() else: for para, value in list(options.__dict__.items()): if value is None: - print("Missing operand:%s"%para) - print("Try %s --help for more information"%sys.argv[0]) + print("Missing operand:%s" % para) + print("Try %s --help for more information" % sys.argv[0]) sys.exit(1) glbEnv.uuid = options.guid @@ -155,7 +187,7 @@ if __name__ == '__main__': try: syscfg.config() print("CloudStack Agent setup is done!") - except (CloudRuntimeException,CloudInternalException) as e: + except (CloudRuntimeException, CloudInternalException) as e: print(e) print("Try to restore your system:") try: diff --git a/agent/bindir/libvirtqemuhook.in b/agent/bindir/libvirtqemuhook.in index e17944d8353..4cc6ed7a1d2 100755 --- a/agent/bindir/libvirtqemuhook.in +++ b/agent/bindir/libvirtqemuhook.in @@ -20,6 +20,19 @@ import sys import os import subprocess from threading import Timer + +# ---- 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 xml.dom.minidom import parse from cloudutils.configFileOps import configFileOps from cloudutils.networkConfig import networkConfig diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index bff7078fd9f..2a083de6fe5 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -213,8 +213,9 @@ hypervisor.type=kvm # If null (default), defaults to the VM's OS architecture #guest.cpu.arch= -# This param will require CPU features on the CPU section. -# The features listed in this property must be separated by a blank space (e.g.: vmx vme) +# Specifies required CPU features for end-user and system VMs. +# These features must be present on the host CPU for VM deployment. +# Multiple features should be separated by whitespace (e.g.: vmx vme). #guest.cpu.features= # Disables memory ballooning on VM guests for overcommit. @@ -441,3 +442,12 @@ iscsi.session.cleanup.enabled=false # Wait(in seconds) during agent reconnections. When no value is set then default value of 5s will be used #backoff.seconds= + +# Timeout (in seconds) to wait for the snapshot reversion to complete. +# revert.snapshot.timeout=10800 + +# Timeout (in seconds) to wait for the incremental snapshot to complete. +# incremental.snapshot.timeout=10800 + +# If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise. +# create.full.clone=false diff --git a/agent/conf/developer.properties.template b/agent/conf/developer.properties.template index a70a136f38c..02f51aa6bb8 100644 --- a/agent/conf/developer.properties.template +++ b/agent/conf/developer.properties.template @@ -5,9 +5,9 @@ # 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 diff --git a/agent/pom.xml b/agent/pom.xml index 76c37cd87be..0fd7d35823e 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT diff --git a/agent/src/main/java/com/cloud/agent/Agent.java b/agent/src/main/java/com/cloud/agent/Agent.java index 0a76bfbb4f8..e86557e9681 100644 --- a/agent/src/main/java/com/cloud/agent/Agent.java +++ b/agent/src/main/java/com/cloud/agent/Agent.java @@ -97,7 +97,6 @@ import com.cloud.utils.nio.Link; import com.cloud.utils.nio.NioClient; import com.cloud.utils.nio.NioConnection; import com.cloud.utils.nio.Task; -import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; /** @@ -342,7 +341,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater logger.info("Attempted to connect to the server, but received an unexpected exception, trying again...", e); } } - shell.updateConnectedHost(); + shell.updateConnectedHost(((NioClient)connection).getHost()); scavengeOldAgentObjects(); } @@ -453,22 +452,30 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater certExecutor.schedule(new PostCertificateRenewalTask(this), 5, TimeUnit.SECONDS); } - private void scheduleHostLBCheckerTask(final long checkInterval) { + private void scheduleHostLBCheckerTask(final String lbAlgorithm, final long checkInterval) { String name = "HostLBCheckerTask"; if (hostLbCheckExecutor != null && !hostLbCheckExecutor.isShutdown()) { + logger.info("Shutting down the preferred host checker task {}", name); hostLbCheckExecutor.shutdown(); try { if (!hostLbCheckExecutor.awaitTermination(1, TimeUnit.SECONDS)) { hostLbCheckExecutor.shutdownNow(); } } catch (InterruptedException e) { - logger.debug("Forcing {} shutdown as it did not shutdown in the desired time due to: {}", + logger.debug("Forcing the preferred host checker task {} shutdown as it did not shutdown in the desired time due to: {}", name, e.getMessage()); hostLbCheckExecutor.shutdownNow(); } } if (checkInterval > 0L) { - logger.info("Scheduling preferred host task with host.lb.interval={}ms", checkInterval); + if ("shuffle".equalsIgnoreCase(lbAlgorithm)) { + logger.info("Scheduling the preferred host checker task to trigger once (to apply lb algorithm '{}') after host.lb.interval={} ms", lbAlgorithm, checkInterval); + hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name))); + hostLbCheckExecutor.schedule(new PreferredHostCheckerTask(), checkInterval, TimeUnit.MILLISECONDS); + return; + } + + logger.info("Scheduling a recurring preferred host checker task with host.lb.interval={} ms", checkInterval); hostLbCheckExecutor = Executors.newSingleThreadScheduledExecutor((new NamedThreadFactory(name))); hostLbCheckExecutor.scheduleAtFixedRate(new PreferredHostCheckerTask(), checkInterval, checkInterval, TimeUnit.MILLISECONDS); @@ -606,9 +613,9 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } protected String getAgentArch() { - final Script command = new Script("/usr/bin/arch", 500, logger); - final OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); - return command.execute(parser); + String arch = Script.runSimpleBashScript(Script.getExecutableAbsolutePath("arch"), 1000); + logger.debug("Arch for agent: {} found: {}", _name, arch); + return arch; } @Override @@ -617,15 +624,11 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } protected void reconnect(final Link link) { - reconnect(link, null, null, false); + reconnect(link, null, false); } - protected void reconnect(final Link link, String preferredHost, List avoidHostList, boolean forTransfer) { + protected void reconnect(final Link link, String preferredMSHost, boolean forTransfer) { if (!(forTransfer || reconnectAllowed)) { - return; - } - - if (!reconnectAllowed) { logger.debug("Reconnect requested but it is not allowed {}", () -> getLinkLog(link)); return; } @@ -637,19 +640,26 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater serverResource.disconnected(); logger.info("Lost connection to host: {}. Attempting reconnection while we still have {} commands in progress.", shell.getConnectedHost(), commandsInProgress.get()); stopAndCleanupConnection(true); + String host = preferredMSHost; + if (org.apache.commons.lang3.StringUtils.isBlank(host)) { + host = shell.getNextHost(); + } + List avoidMSHostList = shell.getAvoidHosts(); do { - final String host = shell.getNextHost(); - connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), shell.getSslHandshakeTimeout(), this); - logger.info("Reconnecting to host: {}", host); - try { - connection.start(); - } catch (final NioConnectionException e) { - logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e); - stopAndCleanupConnection(false); + if (CollectionUtils.isEmpty(avoidMSHostList) || !avoidMSHostList.contains(host)) { + connection = new NioClient(getAgentName(), host, shell.getPort(), shell.getWorkers(), shell.getSslHandshakeTimeout(), this); + logger.info("Reconnecting to host: {}", host); + try { + connection.start(); + } catch (final NioConnectionException e) { + logger.info("Attempted to re-connect to the server, but received an unexpected exception, trying again...", e); + stopAndCleanupConnection(false); + } } shell.getBackoffAlgorithm().waitBeforeRetry(); + host = shell.getNextHost(); } while (!connection.isStartup()); - shell.updateConnectedHost(); + shell.updateConnectedHost(((NioClient)connection).getHost()); logger.info("Connected to the host: {}", shell.getConnectedHost()); } @@ -797,6 +807,9 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } commandsInProgress.incrementAndGet(); try { + if (cmd.isReconcile()) { + cmd.setRequestSequence(request.getSequence()); + } answer = serverResource.executeRequest(cmd); } finally { commandsInProgress.decrementAndGet(); @@ -922,7 +935,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater return new SetupCertificateAnswer(true); } - private void processManagementServerList(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + private void processManagementServerList(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final boolean triggerHostLB) { if (CollectionUtils.isNotEmpty(msList) && StringUtils.isNotEmpty(lbAlgorithm)) { try { final String newMSHosts = String.format("%s%s%s", com.cloud.utils.StringUtils.toCSVList(msList), IAgentShell.hostLbAlgorithmSeparator, lbAlgorithm); @@ -934,24 +947,31 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater throw new CloudRuntimeException("Could not persist received management servers list", e); } } - if ("shuffle".equals(lbAlgorithm)) { - scheduleHostLBCheckerTask(0); - } else { - scheduleHostLBCheckerTask(shell.getLbCheckerInterval(lbCheckInterval)); + shell.setAvoidHosts(avoidMsList); + if (triggerHostLB) { + logger.info("Triggering the preferred host checker task now"); + ScheduledExecutorService hostLbExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("HostLB-Executor")); + hostLbExecutor.schedule(new PreferredHostCheckerTask(), 0, TimeUnit.MILLISECONDS); + hostLbExecutor.shutdown(); } + scheduleHostLBCheckerTask(lbAlgorithm, shell.getLbCheckerInterval(lbCheckInterval)); } private Answer setupManagementServerList(final SetupMSListCommand cmd) { - processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval()); + processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), cmd.getTriggerHostLb()); return new SetupMSListAnswer(true); } private Answer migrateAgentToOtherMS(final MigrateAgentConnectionCommand cmd) { try { if (CollectionUtils.isNotEmpty(cmd.getMsList())) { - processManagementServerList(cmd.getMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval()); + processManagementServerList(cmd.getMsList(), cmd.getAvoidMsList(), cmd.getLbAlgorithm(), cmd.getLbCheckInterval(), false); } - migrateAgentConnection(cmd.getAvoidMsList()); + ScheduledExecutorService migrateAgentConnectionService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("MigrateAgentConnection-Job")); + migrateAgentConnectionService.schedule(() -> { + migrateAgentConnection(cmd.getAvoidMsList()); + }, 3, TimeUnit.SECONDS); + migrateAgentConnectionService.shutdown(); } catch (Exception e) { String errMsg = "Migrate agent connection failed, due to " + e.getMessage(); logger.debug(errMsg, e); @@ -972,25 +992,26 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater throw new CloudRuntimeException("No other Management Server hosts to migrate"); } - String preferredHost = null; + String preferredMSHost = null; for (String msHost : msHostsList) { try (final Socket socket = new Socket()) { socket.connect(new InetSocketAddress(msHost, shell.getPort()), 5000); - preferredHost = msHost; + preferredMSHost = msHost; break; } catch (final IOException e) { throw new CloudRuntimeException("Management server host: " + msHost + " is not reachable, to migrate connection"); } } - if (preferredHost == null) { + if (preferredMSHost == null) { throw new CloudRuntimeException("Management server host(s) are not reachable, to migrate connection"); } - logger.debug("Management server host " + preferredHost + " is found to be reachable, trying to reconnect"); + logger.debug("Management server host " + preferredMSHost + " is found to be reachable, trying to reconnect"); shell.resetHostCounter(); + shell.setAvoidHosts(avoidMsList); shell.setConnectionTransfer(true); - reconnect(link, preferredHost, avoidMsList, true); + reconnect(link, preferredMSHost, true); } public void processResponse(final Response response, final Link link) { @@ -1003,14 +1024,23 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater for (final IAgentControlListener listener : controlListeners) { listener.processControlResponse(response, (AgentControlAnswer)answer); } - } else if (answer instanceof PingAnswer && (((PingAnswer) answer).isSendStartup()) && reconnectAllowed) { - logger.info("Management server requested startup command to reinitialize the agent"); - sendStartup(link); + } else if (answer instanceof PingAnswer) { + processPingAnswer((PingAnswer) answer); } else { updateLastPingResponseTime(); } } + private void processPingAnswer(final PingAnswer answer) { + if ((answer.isSendStartup()) && reconnectAllowed) { + logger.info("Management server requested startup command to reinitialize the agent"); + sendStartup(link); + } else { + serverResource.processPingAnswer((PingAnswer) answer); + } + shell.setAvoidHosts(answer.getAvoidMsList()); + } + public void processReadyCommand(final Command cmd) { final ReadyCommand ready = (ReadyCommand)cmd; // Set human readable sizes; @@ -1027,7 +1057,7 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater } verifyAgentArch(ready.getArch()); - processManagementServerList(ready.getMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval()); + processManagementServerList(ready.getMsHostList(), ready.getAvoidMsHostList(), ready.getLbAlgorithm(), ready.getLbCheckInterval(), false); logger.info("Ready command is processed for agent [id: {}, uuid: {}, name: {}]", getId(), getUuid(), getName()); } @@ -1073,6 +1103,9 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater Answer answer = null; commandsInProgress.incrementAndGet(); try { + if (command.isReconcile()) { + command.setRequestSequence(req.getSequence()); + } answer = serverResource.executeRequest(command); } finally { commandsInProgress.decrementAndGet(); @@ -1374,26 +1407,26 @@ public class Agent implements HandlerFactory, IAgentControl, AgentStatusUpdater if (msList == null || msList.length < 1) { return; } - final String preferredHost = msList[0]; + final String preferredMSHost = msList[0]; final String connectedHost = shell.getConnectedHost(); logger.debug("Running preferred host checker task, connected host={}, preferred host={}", - connectedHost, preferredHost); - if (preferredHost == null || preferredHost.equals(connectedHost) || link == null) { + connectedHost, preferredMSHost); + if (preferredMSHost == null || preferredMSHost.equals(connectedHost) || link == null) { return; } boolean isHostUp = false; try (final Socket socket = new Socket()) { - socket.connect(new InetSocketAddress(preferredHost, shell.getPort()), 5000); + socket.connect(new InetSocketAddress(preferredMSHost, shell.getPort()), 5000); isHostUp = true; } catch (final IOException e) { - logger.debug("Host: {} is not reachable", preferredHost); + logger.debug("Host: {} is not reachable", preferredMSHost); } if (isHostUp && link != null && commandsInProgress.get() == 0) { if (logger.isDebugEnabled()) { - logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredHost); + logger.debug("Preferred host {} is found to be reachable, trying to reconnect", preferredMSHost); } shell.resetHostCounter(); - reconnect(link); + reconnect(link, preferredMSHost, false); } } catch (Throwable t) { logger.error("Error caught while attempting to connect to preferred host", t); diff --git a/agent/src/main/java/com/cloud/agent/AgentShell.java b/agent/src/main/java/com/cloud/agent/AgentShell.java index aea7fd3a8de..4862e7e001e 100644 --- a/agent/src/main/java/com/cloud/agent/AgentShell.java +++ b/agent/src/main/java/com/cloud/agent/AgentShell.java @@ -66,6 +66,7 @@ public class AgentShell implements IAgentShell, Daemon { private String _zone; private String _pod; private String _host; + private List _avoidHosts; private String _privateIp; private int _port; private int _proxyPort; @@ -76,7 +77,6 @@ public class AgentShell implements IAgentShell, Daemon { private volatile boolean _exit = false; private int _pingRetries; private final List _agents = new ArrayList(); - private String hostToConnect; private String connectedHost; private Long preferredHostCheckInterval; private boolean connectionTransfer = false; @@ -121,7 +121,7 @@ public class AgentShell implements IAgentShell, Daemon { if (_hostCounter >= hosts.length) { _hostCounter = 0; } - hostToConnect = hosts[_hostCounter % hosts.length]; + String hostToConnect = hosts[_hostCounter % hosts.length]; _hostCounter++; return hostToConnect; } @@ -143,11 +143,10 @@ public class AgentShell implements IAgentShell, Daemon { } @Override - public void updateConnectedHost() { - connectedHost = hostToConnect; + public void updateConnectedHost(String connectedHost) { + this.connectedHost = connectedHost; } - @Override public void resetHostCounter() { _hostCounter = 0; @@ -166,6 +165,16 @@ public class AgentShell implements IAgentShell, Daemon { } } + @Override + public void setAvoidHosts(List avoidHosts) { + _avoidHosts = avoidHosts; + } + + @Override + public List getAvoidHosts() { + return _avoidHosts; + } + @Override public String getPrivateIp() { return _privateIp; diff --git a/agent/src/main/java/com/cloud/agent/IAgentShell.java b/agent/src/main/java/com/cloud/agent/IAgentShell.java index c0ecd90ae69..9eefa6d2eee 100644 --- a/agent/src/main/java/com/cloud/agent/IAgentShell.java +++ b/agent/src/main/java/com/cloud/agent/IAgentShell.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.agent; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -63,9 +64,13 @@ public interface IAgentShell { String[] getHosts(); + void setAvoidHosts(List hosts); + + List getAvoidHosts(); + long getLbCheckerInterval(Long receivedLbInterval); - void updateConnectedHost(); + void updateConnectedHost(String connectedHost); String getConnectedHost(); diff --git a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java index 61cd27fff77..57dc607b920 100644 --- a/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java +++ b/agent/src/main/java/com/cloud/agent/properties/AgentProperties.java @@ -155,6 +155,14 @@ public class AgentProperties{ */ public static final Property CMDS_TIMEOUT = new Property<>("cmds.timeout", 7200); + /** + * The timeout (in seconds) for the snapshot merge operation, mainly used for classic volume snapshots and disk-only VM snapshots on file-based storage.
+ * This configuration is only considered if libvirt.events.enabled is also true.
+ * Data type: Integer.
+ * Default value: 259200 + */ + public static final Property QCOW2_DELTA_MERGE_TIMEOUT = new Property<>("qcow2.delta.merge.timeout", 60 * 60 * 72); + /** * This parameter sets the VM migration speed (in mbps). The default value is -1,
* which means that the agent will try to guess the speed of the guest network and consume all possible bandwidth.
@@ -213,6 +221,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.script", "libvirt-vm-xml-transformer.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM XML transformer shell script.
+ * The shell script is used to execute the Libvirt VM XML transformer script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-xml-transformer.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_XML_TRANSFORMER_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_xml_transformer.shell_script", "libvirt-vm-xml-transformer.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_xml_transformer.script properties to define the Libvirt VM XML transformer method.
* Libvirt XML transformer hook does XML-to-XML transformation.
@@ -233,6 +250,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on start shell script.
+ * The shell script is used to execute the Libvirt VM on start script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_START_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_start.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_start.script properties to define the Libvirt VM on start method.
* The hook is called right after Libvirt successfully launched the VM.
@@ -252,6 +278,15 @@ public class AgentProperties{ */ public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.script", "libvirt-vm-state-change.groovy"); + /** + * This property is used with the agent.hooks.basedir property to define the Libvirt VM on stop shell script.
+ * The shell script is used to execute the Libvirt VM on stop script.
+ * For more information see the agent.properties file.
+ * Data type: String.
+ * Default value: libvirt-vm-state-change.sh + */ + public static final Property AGENT_HOOKS_LIBVIRT_VM_ON_STOP_SHELL_SCRIPT = new Property<>("agent.hooks.libvirt_vm_on_stop.shell_script", "libvirt-vm-state-change.sh"); + /** * This property is used with the agent.hooks.basedir and agent.hooks.libvirt_vm_on_stop.script properties to define the Libvirt VM on stop method.
* The hook is called right after libvirt successfully stopped the VM.
@@ -390,8 +425,9 @@ public class AgentProperties{ public static final Property GUEST_CPU_ARCH = new Property<>("guest.cpu.arch", null, String.class); /** - * This param will require CPU features on the CPU section.
- * The features listed in this property must be separated by a blank space (see example below).
+ * Specifies required CPU features for end-user and system VMs.
+ * These features must be present on the host CPU for VM deployment.
+ * Multiple features should be separated by whitespace (see example below).
* Possible values: vmx vme
* Data type: String.
* Default value: null @@ -816,14 +852,32 @@ public class AgentProperties{ * Data type: Integer.
* Default value: null */ - public static final Property SSL_HANDSHAKE_TIMEOUT = new Property<>("ssl.handshake.timeout", null, Integer.class); + public static final Property SSL_HANDSHAKE_TIMEOUT = new Property<>("ssl.handshake.timeout", 30, Integer.class); + + /** + * Timeout (in seconds) to wait for the incremental snapshot to complete. + * */ + public static final Property INCREMENTAL_SNAPSHOT_TIMEOUT = new Property<>("incremental.snapshot.timeout", 10800); + + /** + * Timeout (in seconds) to wait for the snapshot reversion to complete. + * */ + public static final Property REVERT_SNAPSHOT_TIMEOUT = new Property<>("revert.snapshot.timeout", 10800); + + /** + * If set to true, creates VMs as full clones of their templates on KVM hypervisor. Creates as linked clones otherwise.
+ * Data type: Boolean.
+ * Default value: false + */ + public static final Property CREATE_FULL_CLONE = new Property<>("create.full.clone", false); + public static class Property { private String name; private T defaultValue; private Class typeClass; - Property(String name, T value) { + public Property(String name, T value) { init(name, value); } diff --git a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java index fe519ca9497..4002e53b585 100644 --- a/agent/src/main/java/com/cloud/agent/resource/DummyResource.java +++ b/agent/src/main/java/com/cloud/agent/resource/DummyResource.java @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; @@ -40,6 +39,7 @@ import com.cloud.resource.ServerResource; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.StringUtils; +import com.cloud.utils.UuidUtils; public class DummyResource implements ServerResource { String _name; @@ -133,7 +133,7 @@ public class DummyResource implements ServerResource { String hostIp = getConfiguredProperty("private.ip.address", "127.0.0.1"); String localStoragePath = getConfiguredProperty("local.storage.path", "/mnt"); String lh = hostIp + localStoragePath; - String uuid = UUID.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString(); + String uuid = UuidUtils.nameUUIDFromBytes(lh.getBytes(StringUtils.getPreferredCharset())).toString(); String capacity = getConfiguredProperty("local.storage.capacity", "1000000000"); String available = getConfiguredProperty("local.storage.avail", "10000000"); diff --git a/agent/src/test/java/com/cloud/agent/AgentShellTest.java b/agent/src/test/java/com/cloud/agent/AgentShellTest.java index 6d9758cc3dc..d8def24a603 100644 --- a/agent/src/test/java/com/cloud/agent/AgentShellTest.java +++ b/agent/src/test/java/com/cloud/agent/AgentShellTest.java @@ -358,7 +358,7 @@ public class AgentShellTest { AgentShell shell = new AgentShell(); shell.setHosts("test"); shell.getNextHost(); - shell.updateConnectedHost(); + shell.updateConnectedHost("test"); Assert.assertEquals(expected, shell.getConnectedHost()); } diff --git a/api/pom.xml b/api/pom.xml index ec68e24c7e5..6270eb2dead 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT diff --git a/api/src/main/java/com/cloud/agent/api/Command.java b/api/src/main/java/com/cloud/agent/api/Command.java index eb979c0060b..c4e99cb4170 100644 --- a/api/src/main/java/com/cloud/agent/api/Command.java +++ b/api/src/main/java/com/cloud/agent/api/Command.java @@ -19,9 +19,10 @@ package com.cloud.agent.api; import java.util.HashMap; import java.util.Map; -import com.cloud.agent.api.LogLevel.Log4jLevel; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.cloud.agent.api.LogLevel.Log4jLevel; /** * implemented by classes that extends the Command class. Command specifies @@ -35,6 +36,23 @@ public abstract class Command { Continue, Stop } + public enum State { + CREATED, // Command is created by management server + STARTED, // Command is started by agent + PROCESSING, // Processing by agent + PROCESSING_IN_BACKEND, // Processing in backend by agent + COMPLETED, // Operation succeeds by agent or management server + FAILED, // Operation fails by agent + RECONCILE_RETRY, // Ready for retry of reconciliation + RECONCILING, // Being reconciled by management server + RECONCILED, // Reconciled by management server + RECONCILE_SKIPPED, // Skip the reconciliation as the resource state is inconsistent with the command + RECONCILE_FAILED, // Fail to reconcile by management server + TIMED_OUT, // Timed out on management server or agent + INTERRUPTED, // Interrupted by management server or agent (for example agent is restarted), + DANGLED_IN_BACKEND // Backend process which cannot be processed normally (for example agent is restarted) + } + public static final String HYPERVISOR_TYPE = "hypervisorType"; // allow command to carry over hypervisor or other environment related context info @@ -42,6 +60,8 @@ public abstract class Command { protected Map contextMap = new HashMap(); private int wait; //in second private boolean bypassHostMaintenance = false; + private transient long requestSequence = 0L; + protected Map> externalDetails; protected Command() { this.wait = 0; @@ -82,6 +102,10 @@ public abstract class Command { return contextMap.get(name); } + public Map getContextMap() { + return contextMap; + } + public boolean allowCaching() { return true; } @@ -94,6 +118,26 @@ public abstract class Command { this.bypassHostMaintenance = bypassHostMaintenance; } + public boolean isReconcile() { + return false; + } + + public long getRequestSequence() { + return requestSequence; + } + + public void setRequestSequence(long requestSequence) { + this.requestSequence = requestSequence; + } + + public void setExternalDetails(Map> externalDetails) { + this.externalDetails = externalDetails; + } + + public Map> getExternalDetails() { + return externalDetails; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java index 85ffc189820..5515a9c48bc 100644 --- a/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java +++ b/api/src/main/java/com/cloud/agent/api/VgpuTypesInfo.java @@ -15,10 +15,24 @@ // specific language governing permissions and limitations // under the License. package com.cloud.agent.api; + +import org.apache.cloudstack.gpu.GpuDevice; + public class VgpuTypesInfo { + private boolean passthroughEnabled = true; + private GpuDevice.DeviceType deviceType; + private String parentBusAddress; + private String busAddress; + private String numaNode; + private String pciRoot; + private String deviceId; + private String deviceName; + private String vendorId; + private String vendorName; private String modelName; private String groupName; + private String vmName; private Long maxHeads; private Long videoRam; private Long maxResolutionX; @@ -26,6 +40,7 @@ public class VgpuTypesInfo { private Long maxVgpuPerGpu; private Long remainingCapacity; private Long maxCapacity; + private boolean display = false; public String getModelName() { return modelName; @@ -39,22 +54,42 @@ public class VgpuTypesInfo { return videoRam; } + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + public Long getMaxHeads() { return maxHeads; } + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + public Long getMaxResolutionX() { return maxResolutionX; } + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + public Long getMaxResolutionY() { return maxResolutionY; } + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + public Long getMaxVpuPerGpu() { return maxVgpuPerGpu; } + public void setMaxVgpuPerGpu(Long maxVgpuPerGpu) { + this.maxVgpuPerGpu = maxVgpuPerGpu; + } + public Long getRemainingCapacity() { return remainingCapacity; } @@ -71,8 +106,133 @@ public class VgpuTypesInfo { this.maxCapacity = maxCapacity; } - public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerGpu, - Long remainingCapacity, Long maxCapacity) { + public boolean isPassthroughEnabled() { + return passthroughEnabled; + } + + public void setPassthroughEnabled(boolean passthroughEnabled) { + this.passthroughEnabled = passthroughEnabled; + } + + public GpuDevice.DeviceType getDeviceType() { + return deviceType; + } + + public void setDeviceType(GpuDevice.DeviceType deviceType) { + this.deviceType = deviceType; + } + + public String getParentBusAddress() { + return parentBusAddress; + } + + public void setParentBusAddress(String parentBusAddress) { + this.parentBusAddress = parentBusAddress; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public boolean isDisplay() { + return display; + } + + public void setDisplay(boolean display) { + this.display = display; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName, String numaNode, String pciRoot + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + public VgpuTypesInfo(GpuDevice.DeviceType deviceType, String groupName, String modelName, String busAddress, + String vendorId, String vendorName, String deviceId, String deviceName + ) { + this.deviceType = deviceType; + this.groupName = groupName; + this.modelName = modelName; + this.busAddress = busAddress; + this.deviceId = deviceId; + this.deviceName = deviceName; + this.vendorId = vendorId; + this.vendorName = vendorName; + } + + public VgpuTypesInfo(String groupName, String modelName, Long videoRam, Long maxHeads, Long maxResolutionX, + Long maxResolutionY, Long maxVgpuPerGpu, Long remainingCapacity, Long maxCapacity + ) { this.groupName = groupName; this.modelName = modelName; this.videoRam = videoRam; diff --git a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java index d22df2df172..5664de79091 100644 --- a/api/src/main/java/com/cloud/agent/api/to/DiskTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/DiskTO.java @@ -46,7 +46,7 @@ public class DiskTO { private Long diskSeq; private String path; private Volume.Type type; - private Map _details; + private Map details; public DiskTO() { @@ -92,10 +92,10 @@ public class DiskTO { } public void setDetails(Map details) { - _details = details; + this.details = details; } public Map getDetails() { - return _details; + return details; } } diff --git a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java index 25c75001a3c..69350815be3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/FirewallRuleTO.java @@ -47,7 +47,7 @@ public class FirewallRuleTO implements InternalIdentity { int[] srcPortRange; boolean revoked; boolean alreadyAdded; - private List sourceCidrList; + protected List sourceCidrList; private List destCidrList; FirewallRule.Purpose purpose; private Integer icmpType; diff --git a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java index 4afe080477b..6e9cee06dd3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/GPUDeviceTO.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.agent.api.to; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.VgpuTypesInfo; @@ -24,9 +26,23 @@ public class GPUDeviceTO { private String gpuGroup; private String vgpuType; + private int gpuCount; private HashMap> groupDetails = new HashMap>(); + private List gpuDevices = new ArrayList<>(); - public GPUDeviceTO( String gpuGroup, String vgpuType, HashMap> groupDetails) { + public GPUDeviceTO(String gpuGroup, String vgpuType, int gpuCount, + HashMap> groupDetails, + List gpuDevices) { + this.gpuGroup = gpuGroup; + this.vgpuType = vgpuType; + this.groupDetails = groupDetails; + this.gpuCount = gpuCount; + this.gpuDevices = gpuDevices; + + } + + public GPUDeviceTO(String gpuGroup, String vgpuType, + HashMap> groupDetails) { this.gpuGroup = gpuGroup; this.vgpuType = vgpuType; this.groupDetails = groupDetails; @@ -48,6 +64,14 @@ public class GPUDeviceTO { this.vgpuType = vgpuType; } + public int getGpuCount() { + return gpuCount; + } + + public void setGpuCount(int gpuCount) { + this.gpuCount = gpuCount; + } + public HashMap> getGroupDetails() { return groupDetails; } @@ -56,4 +80,11 @@ public class GPUDeviceTO { this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java index f395f26aeed..6c4b9e607c5 100644 --- a/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/LoadBalancerTO.java @@ -71,7 +71,7 @@ public class LoadBalancerTO { this.destinations = new DestinationTO[destinations.size()]; this.stickinessPolicies = null; this.sslCert = null; - this.lbProtocol = null; + this.lbProtocol = protocol; int i = 0; for (LbDestination destination : destinations) { this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false); @@ -205,6 +205,10 @@ public class LoadBalancerTO { return this.sslCert; } + public void setLbSslCert(LbSslCert sslCert) { + this.sslCert = sslCert; + } + public String getSrcIpVlan() { return srcIpVlan; } diff --git a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java index bd08ce81101..d65ec0e3daa 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java @@ -36,7 +36,7 @@ public class NetworkTO { protected TrafficType type; protected URI broadcastUri; protected URI isolationUri; - protected boolean isSecurityGroupEnabled; + protected boolean securityGroupEnabled; protected String name; protected String ip6address; protected String ip6gateway; @@ -112,7 +112,7 @@ public class NetworkTO { } public void setSecurityGroupEnabled(boolean enabled) { - this.isSecurityGroupEnabled = enabled; + this.securityGroupEnabled = enabled; } /** @@ -221,7 +221,7 @@ public class NetworkTO { } public boolean isSecurityGroupEnabled() { - return this.isSecurityGroupEnabled; + return this.securityGroupEnabled; } public void setIp6Dns1(String ip6Dns1) { diff --git a/api/src/main/java/com/cloud/agent/api/to/NicTO.java b/api/src/main/java/com/cloud/agent/api/to/NicTO.java index 573363c04fb..ca95fcfd679 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java @@ -86,6 +86,14 @@ public class NicTO extends NetworkTO { this.nicUuid = uuid; } + public String getNicUuid() { + return nicUuid; + } + + public void setNicUuid(String nicUuid) { + this.nicUuid = nicUuid; + } + @Override public String toString() { return new StringBuilder("[Nic:").append(type).append("-").append(ip).append("-").append(broadcastUri).append("]").toString(); diff --git a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java index d43625c09a9..91f337c5f55 100644 --- a/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/PortForwardingRuleTO.java @@ -21,8 +21,6 @@ import com.cloud.network.rules.PortForwardingRule; import com.cloud.utils.net.NetUtils; import org.apache.commons.lang3.StringUtils; -import java.util.List; - /** * PortForwardingRuleTO specifies one port forwarding rule. * @@ -32,8 +30,6 @@ public class PortForwardingRuleTO extends FirewallRuleTO { String dstIp; int[] dstPortRange; - List sourceCidrList; - protected PortForwardingRuleTO() { super(); } diff --git a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java index d86eb2a3a7f..18737c584b3 100644 --- a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java @@ -27,6 +27,7 @@ public class RemoteInstanceTO implements Serializable { private Hypervisor.HypervisorType hypervisorType; private String instanceName; + private String instancePath; // VMware Remote Instances parameters (required for exporting OVA through ovftool) // TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes @@ -44,9 +45,10 @@ public class RemoteInstanceTO implements Serializable { this.instanceName = instanceName; } - public RemoteInstanceTO(String instanceName, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { + public RemoteInstanceTO(String instanceName, String instancePath, String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) { this.hypervisorType = Hypervisor.HypervisorType.VMware; this.instanceName = instanceName; + this.instancePath = instancePath; this.vcenterHost = vcenterHost; this.vcenterUsername = vcenterUsername; this.vcenterPassword = vcenterPassword; @@ -61,6 +63,10 @@ public class RemoteInstanceTO implements Serializable { return this.instanceName; } + public String getInstancePath() { + return this.instancePath; + } + public String getVcenterUsername() { return vcenterUsername; } diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java index 6f24b1cd6ca..cffb9874080 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -19,20 +19,22 @@ package com.cloud.agent.api.to; import java.util.List; import java.util.Map; import java.util.HashMap; +import java.util.stream.Collectors; import com.cloud.agent.api.LogLevel; import com.cloud.network.element.NetworkElement; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VmDetailConstants; public class VirtualMachineTO { private long id; private String name; private BootloaderType bootloader; private VirtualMachine.State state; - Type type; - int cpus; + private Type type; + private int cpus; /** 'speed' is still here since 4.0.X/4.1.X management servers do not support @@ -43,49 +45,50 @@ public class VirtualMachineTO { So this is here for backwards compatibility with 4.0.X/4.1.X management servers and newer agents. */ - Integer speed; - Integer minSpeed; - Integer maxSpeed; + private Integer speed; + private Integer minSpeed; + private Integer maxSpeed; - long minRam; - long maxRam; - String hostName; - String arch; - String os; - String platformEmulator; - String bootArgs; - String[] bootupScripts; - boolean enableHA; - boolean limitCpuUse; - boolean enableDynamicallyScaleVm; + private long minRam; + private long maxRam; + private String hostName; + private String arch; + private String os; + private String platformEmulator; + private String bootArgs; + private String[] bootupScripts; + private boolean enableHA; + private boolean limitCpuUse; + private boolean enableDynamicallyScaleVm; @LogLevel(LogLevel.Log4jLevel.Off) - String vncPassword; - String vncAddr; - Map params; - String uuid; - String bootType; - String bootMode; - boolean enterHardwareSetup; + private String vncPassword; + private String vncAddr; + private Map details; + private Map params; + private String uuid; + private String bootType; + private String bootMode; + private boolean enterHardwareSetup; - DiskTO[] disks; - NicTO[] nics; - GPUDeviceTO gpuDevice; - Integer vcpuMaxLimit; - List vmData = null; + private DiskTO[] disks; + private NicTO[] nics; + private GPUDeviceTO gpuDevice; + private Integer vcpuMaxLimit; + private List vmData = null; - String configDriveLabel = null; - String configDriveIsoRootFolder = null; - String configDriveIsoFile = null; - NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; + private String configDriveLabel = null; + private String configDriveIsoRootFolder = null; + private String configDriveIsoFile = null; + private NetworkElement.Location configDriveLocation = NetworkElement.Location.SECONDARY; - Double cpuQuotaPercentage = null; + private Double cpuQuotaPercentage = null; - Map guestOsDetails = new HashMap(); - Map extraConfig = new HashMap<>(); - Map networkIdToNetworkNameMap = new HashMap<>(); - DeployAsIsInfoTO deployAsIsInfo; - String metadataManufacturer; - String metadataProductName; + private Map guestOsDetails = new HashMap(); + private Map extraConfig = new HashMap<>(); + private Map networkIdToNetworkNameMap = new HashMap<>(); + private DeployAsIsInfoTO deployAsIsInfo; + private String metadataManufacturer; + private String metadataProductName; public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, String os, boolean enableHA, boolean limitCpuUse, String vncPassword) { @@ -191,7 +194,11 @@ public class VirtualMachineTO { return maxSpeed; } - public boolean getLimitCpuUse() { + public boolean isEnableHA() { + return enableHA; + } + + public boolean isLimitCpuUse() { return limitCpuUse; } @@ -256,6 +263,10 @@ public class VirtualMachineTO { this.bootupScripts = bootupScripts; } + public void setEnableHA(boolean enableHA) { + this.enableHA = enableHA; + } + public DiskTO[] getDisks() { return disks; } @@ -289,11 +300,11 @@ public class VirtualMachineTO { } public Map getDetails() { - return params; + return details; } public void setDetails(Map params) { - this.params = params; + this.details = params; } public String getUuid() { @@ -431,6 +442,42 @@ public class VirtualMachineTO { this.deployAsIsInfo = deployAsIsInfo; } + public void setSpeed(Integer speed) { + this.speed = speed; + } + + public void setMinSpeed(Integer minSpeed) { + this.minSpeed = minSpeed; + } + + public void setMaxSpeed(Integer maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public void setMinRam(long minRam) { + this.minRam = minRam; + } + + public void setMaxRam(long maxRam) { + this.maxRam = maxRam; + } + + public void setLimitCpuUse(boolean limitCpuUse) { + this.limitCpuUse = limitCpuUse; + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + + public void setExtraConfig(Map extraConfig) { + this.extraConfig = extraConfig; + } + public String getMetadataManufacturer() { return metadataManufacturer; } @@ -451,4 +498,16 @@ public class VirtualMachineTO { public String toString() { return String.format("VM {id: \"%s\", name: \"%s\", uuid: \"%s\", type: \"%s\"}", id, name, uuid, type); } + + public Map getExternalDetails() { + if (details == null) { + return new HashMap<>(); + } + return details.entrySet().stream() + .filter(entry -> entry.getKey().startsWith(VmDetailConstants.EXTERNAL_DETAIL_PREFIX)) + .collect(Collectors.toMap( + entry -> entry.getKey().substring(VmDetailConstants.EXTERNAL_DETAIL_PREFIX.length()), + Map.Entry::getValue + )); + } } diff --git a/api/src/main/java/com/cloud/capacity/Capacity.java b/api/src/main/java/com/cloud/capacity/Capacity.java index a4e2c2a7f05..4e584b18fee 100644 --- a/api/src/main/java/com/cloud/capacity/Capacity.java +++ b/api/src/main/java/com/cloud/capacity/Capacity.java @@ -34,13 +34,17 @@ public interface Capacity extends InternalIdentity, Identity { public static final short CAPACITY_TYPE_LOCAL_STORAGE = 9; public static final short CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET = 10; public static final short CAPACITY_TYPE_GPU = 19; + public static final short CAPACITY_TYPE_OBJECT_STORAGE = 20; + public static final short CAPACITY_TYPE_BACKUP_STORAGE = 21; public static final short CAPACITY_TYPE_CPU_CORE = 90; public static final List STORAGE_CAPACITY_TYPES = List.of(CAPACITY_TYPE_STORAGE, CAPACITY_TYPE_STORAGE_ALLOCATED, CAPACITY_TYPE_SECONDARY_STORAGE, - CAPACITY_TYPE_LOCAL_STORAGE); + CAPACITY_TYPE_LOCAL_STORAGE, + CAPACITY_TYPE_BACKUP_STORAGE, + CAPACITY_TYPE_OBJECT_STORAGE); public Long getHostOrPoolId(); diff --git a/api/src/main/java/com/cloud/configuration/ConfigurationService.java b/api/src/main/java/com/cloud/configuration/ConfigurationService.java index 97d4b42974b..32e31519ea7 100644 --- a/api/src/main/java/com/cloud/configuration/ConfigurationService.java +++ b/api/src/main/java/com/cloud/configuration/ConfigurationService.java @@ -17,7 +17,11 @@ package com.cloud.configuration; import java.util.List; +import java.util.Map; +import java.util.Objects; +import com.cloud.network.Network; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd; import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; import org.apache.cloudstack.api.command.admin.network.CreateGuestNetworkIpv6PrefixCmd; @@ -201,11 +205,12 @@ public interface ConfigurationService { * TODO * @param allocationState * TODO + * @param storageAccessGroups * @return the new pod if successful, null otherwise * @throws * @throws */ - Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState); + Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState, List storageAccessGroups); /** * Creates a mutual exclusive IP range in the pod with same gateway, netmask. @@ -372,4 +377,16 @@ public interface ConfigurationService { List listPortableIps(long id); Boolean isAccountAllowedToCreateOfferingsWithTags(IsAccountAllowedToCreateOfferingsWithTagsCmd cmd); + + public static final Map ProviderDetailKeyMap = Map.of( + Network.Provider.Nsx.getName(), ApiConstants.NSX_DETAIL_KEY, + Network.Provider.Netris.getName(), ApiConstants.NETRIS_DETAIL_KEY + ); + + public static boolean IsIpRangeForProvider(Network.Provider provider) { + if (Objects.isNull(provider)) { + return false; + } + return ProviderDetailKeyMap.containsKey(provider.getName()); + } } diff --git a/api/src/main/java/com/cloud/configuration/Resource.java b/api/src/main/java/com/cloud/configuration/Resource.java index c7bf44de76c..97be7f9d64c 100644 --- a/api/src/main/java/com/cloud/configuration/Resource.java +++ b/api/src/main/java/com/cloud/configuration/Resource.java @@ -37,7 +37,8 @@ public interface Resource { backup("backup", 12), backup_storage("backup_storage", 13), bucket("bucket", 14), - object_storage("object_storage", 15); + object_storage("object_storage", 15), + gpu("gpu", 16); private String name; private int ordinal; diff --git a/api/src/main/java/com/cloud/cpu/CPU.java b/api/src/main/java/com/cloud/cpu/CPU.java index 4e1b9f5a501..3016e542db6 100644 --- a/api/src/main/java/com/cloud/cpu/CPU.java +++ b/api/src/main/java/com/cloud/cpu/CPU.java @@ -16,52 +16,55 @@ // under the License. package com.cloud.cpu; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.lang3.StringUtils; -import java.util.LinkedHashMap; -import java.util.Map; - public class CPU { + public enum CPUArch { + x86("i686", 32), + amd64("x86_64", 64), + arm64("aarch64", 64); - public static final String archX86Identifier = "i686"; - public static final String archX86_64Identifier = "x86_64"; - public static final String archARM64Identifier = "aarch64"; + private final String type; + private final int bits; - public static class CPUArch { - private static final Map cpuArchMap = new LinkedHashMap<>(); - - public static final CPUArch archX86 = new CPUArch(archX86Identifier, 32); - public static final CPUArch amd64 = new CPUArch(archX86_64Identifier, 64); - public static final CPUArch arm64 = new CPUArch(archARM64Identifier, 64); - - private String type; - private int bits; - - public CPUArch(String type, int bits) { + CPUArch(String type, int bits) { this.type = type; this.bits = bits; - cpuArchMap.put(type, this); + } + + public static CPUArch getDefault() { + return amd64; } public String getType() { - return this.type; + return type; } public int getBits() { - return this.bits; + return bits; } public static CPUArch fromType(String type) { if (StringUtils.isBlank(type)) { - return amd64; + return getDefault(); } - switch (type) { - case archX86Identifier: return archX86; - case archX86_64Identifier: return amd64; - case archARM64Identifier: return arm64; - default: throw new CloudRuntimeException(String.format("Unsupported arch type: %s", type)); + for (CPUArch arch : values()) { + if (arch.type.equals(type)) { + return arch; + } } + throw new IllegalArgumentException("Unsupported arch type: " + type); + } + + public static String getTypesAsCSV() { + StringBuilder sb = new StringBuilder(); + for (CPUArch arch : values()) { + sb.append(arch.getType()).append(","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); } } } diff --git a/api/src/main/java/com/cloud/dc/Pod.java b/api/src/main/java/com/cloud/dc/Pod.java index 1cbab36f3bd..17c5b615d4b 100644 --- a/api/src/main/java/com/cloud/dc/Pod.java +++ b/api/src/main/java/com/cloud/dc/Pod.java @@ -43,4 +43,6 @@ public interface Pod extends InfrastructureEntity, Grouping, Identity, InternalI AllocationState getAllocationState(); boolean getExternalDhcp(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java index 2697311d2b9..d127e4bdd66 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java @@ -62,7 +62,7 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { "vm.allocation.algorithm", "Advanced", "random", - "Order in which hosts within a cluster will be considered for VM/volume allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.", + "Order in which hosts within a cluster will be considered for VM allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.Select, diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 862a6e21fa8..be21f13267b 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -29,13 +29,18 @@ import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionCustomAction; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.cloudstack.gpu.VgpuProfile; import org.apache.cloudstack.ha.HAConfig; import org.apache.cloudstack.network.BgpPeer; import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap; import org.apache.cloudstack.quota.QuotaTariff; -import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.storage.object.Bucket; import org.apache.cloudstack.storage.object.ObjectStore; +import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.vm.schedule.VMSchedule; @@ -289,6 +294,8 @@ public class EventTypes { //registering userdata events public static final String EVENT_REGISTER_USER_DATA = "REGISTER.USER.DATA"; + public static final String EVENT_REGISTER_CNI_CONFIG = "REGISTER.CNI.CONFIG"; + public static final String EVENT_DELETE_CNI_CONFIG = "DELETE.CNI.CONFIG"; //register for user API and secret keys public static final String EVENT_REGISTER_FOR_SECRET_API_KEY = "REGISTER.USER.KEY"; @@ -374,6 +381,21 @@ public class EventTypes { public static final String EVENT_DISK_OFFERING_EDIT = "DISK.OFFERING.EDIT"; public static final String EVENT_DISK_OFFERING_DELETE = "DISK.OFFERING.DELETE"; + // GPU Cards + public static final String EVENT_GPU_CARD_CREATE = "GPU.CARD.CREATE"; + public static final String EVENT_GPU_CARD_EDIT = "GPU.CARD.EDIT"; + public static final String EVENT_GPU_CARD_DELETE = "GPU.CARD.DELETE"; + + // vGPU Profile + public static final String EVENT_VGPU_PROFILE_CREATE = "VGPU.PROFILE.CREATE"; + public static final String EVENT_VGPU_PROFILE_EDIT = "VGPU.PROFILE.EDIT"; + public static final String EVENT_VGPU_PROFILE_DELETE = "VGPU.PROFILE.DELETE"; + + // GPU Devices + public static final String EVENT_GPU_DEVICE_CREATE = "GPU.DEVICE.CREATE"; + public static final String EVENT_GPU_DEVICE_EDIT = "GPU.DEVICE.EDIT"; + public static final String EVENT_GPU_DEVICE_DELETE = "GPU.DEVICE.DELETE"; + // Network offerings public static final String EVENT_NETWORK_OFFERING_CREATE = "NETWORK.OFFERING.CREATE"; public static final String EVENT_NETWORK_OFFERING_ASSIGN = "NETWORK.OFFERING.ASSIGN"; @@ -465,6 +487,7 @@ public class EventTypes { public static final String EVENT_ENABLE_PRIMARY_STORAGE = "ENABLE.PS"; public static final String EVENT_DISABLE_PRIMARY_STORAGE = "DISABLE.PS"; public static final String EVENT_SYNC_STORAGE_POOL = "SYNC.STORAGE.POOL"; + public static final String EVENT_CONFIGURE_STORAGE_ACCESS = "CONFIGURE.STORAGE.ACCESS"; public static final String EVENT_CHANGE_STORAGE_POOL_SCOPE = "CHANGE.STORAGE.POOL.SCOPE"; // VPN @@ -496,6 +519,8 @@ public class EventTypes { public static final String EVENT_ZONE_VLAN_ASSIGN = "ZONE.VLAN.ASSIGN"; public static final String EVENT_ZONE_VLAN_RELEASE = "ZONE.VLAN.RELEASE"; + public static final String EVENT_ZONE_VXLAN_ASSIGN = "ZONE.VXLAN.ASSIGN"; + public static final String EVENT_ZONE_VXLAN_RELEASE = "ZONE.VXLAN.RELEASE"; // Projects public static final String EVENT_PROJECT_CREATE = "PROJECT.CREATE"; @@ -607,11 +632,13 @@ public class EventTypes { public static final String EVENT_VM_BACKUP_CREATE = "BACKUP.CREATE"; public static final String EVENT_VM_BACKUP_RESTORE = "BACKUP.RESTORE"; public static final String EVENT_VM_BACKUP_DELETE = "BACKUP.DELETE"; + public static final String EVENT_VM_BACKUP_OFFERING_REMOVED_AND_BACKUPS_DELETED = "BACKUP.OFFERING.BACKUPS.DEL"; public static final String EVENT_VM_BACKUP_RESTORE_VOLUME_TO_VM = "BACKUP.RESTORE.VOLUME.TO.VM"; public static final String EVENT_VM_BACKUP_SCHEDULE_CONFIGURE = "BACKUP.SCHEDULE.CONFIGURE"; public static final String EVENT_VM_BACKUP_SCHEDULE_DELETE = "BACKUP.SCHEDULE.DELETE"; public static final String EVENT_VM_BACKUP_USAGE_METRIC = "BACKUP.USAGE.METRIC"; public static final String EVENT_VM_BACKUP_EDIT = "BACKUP.OFFERING.EDIT"; + public static final String EVENT_VM_CREATE_FROM_BACKUP = "VM.CREATE.FROM.BACKUP"; // external network device events public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD"; @@ -687,6 +714,9 @@ public class EventTypes { public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE"; //Guest OS related events + public static final String EVENT_GUEST_OS_CATEGORY_ADD = "GUEST.OS.CATEGORY.ADD"; + public static final String EVENT_GUEST_OS_CATEGORY_DELETE = "GUEST.OS.CATEGORY.DELETE"; + public static final String EVENT_GUEST_OS_CATEGORY_UPDATE = "GUEST.OS.CATEGORY.UPDATE"; public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD"; public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE"; public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE"; @@ -739,6 +769,13 @@ public class EventTypes { //Purge resources public static final String EVENT_PURGE_EXPUNGED_RESOURCES = "PURGE.EXPUNGED.RESOURCES"; + // Management Server + public static final String EVENT_MS_MAINTENANCE_PREPARE = "MS.MAINTENANCE.PREPARE"; + public static final String EVENT_MS_MAINTENANCE_CANCEL = "MS.MAINTENANCE.CANCEL"; + public static final String EVENT_MS_SHUTDOWN_PREPARE = "MS.SHUTDOWN.PREPARE"; + public static final String EVENT_MS_SHUTDOWN_CANCEL = "MS.SHUTDOWN.CANCEL"; + public static final String EVENT_MS_SHUTDOWN = "MS.SHUTDOWN"; + // OBJECT STORE public static final String EVENT_OBJECT_STORE_CREATE = "OBJECT.STORE.CREATE"; public static final String EVENT_OBJECT_STORE_DELETE = "OBJECT.STORE.DELETE"; @@ -788,6 +825,33 @@ public class EventTypes { // Resource Limit public static final String EVENT_RESOURCE_LIMIT_UPDATE = "RESOURCE.LIMIT.UPDATE"; + // Management Server + public static final String EVENT_MANAGEMENT_SERVER_REMOVE = "MANAGEMENT.SERVER.REMOVE"; + + // VM Lease + public static final String VM_LEASE_EXPIRED = "VM.LEASE.EXPIRED"; + public static final String VM_LEASE_DISABLED = "VM.LEASE.DISABLED"; + public static final String VM_LEASE_CANCELLED = "VM.LEASE.CANCELLED"; + public static final String VM_LEASE_EXPIRING = "VM.LEASE.EXPIRING"; + + // GUI Theme + public static final String EVENT_GUI_THEME_CREATE = "GUI.THEME.CREATE"; + public static final String EVENT_GUI_THEME_REMOVE = "GUI.THEME.REMOVE"; + public static final String EVENT_GUI_THEME_UPDATE = "GUI.THEME.UPDATE"; + + // Extension + public static final String EVENT_EXTENSION_CREATE = "EXTENSION.CREATE"; + public static final String EVENT_EXTENSION_UPDATE = "EXTENSION.UPDATE"; + public static final String EVENT_EXTENSION_DELETE = "EXTENSION.DELETE"; + public static final String EVENT_EXTENSION_RESOURCE_REGISTER = "EXTENSION.RESOURCE.REGISTER"; + public static final String EVENT_EXTENSION_RESOURCE_UNREGISTER = "EXTENSION.RESOURCE.UNREGISTER"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_ADD = "EXTENSION.CUSTOM.ACTION.ADD"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_UPDATE = "EXTENSION.CUSTOM.ACTION.UPDATE"; + public static final String EVENT_EXTENSION_CUSTOM_ACTION_DELETE = "EXTENSION.CUSTOM.ACTION.DELETE"; + + // Custom Action + public static final String EVENT_CUSTOM_ACTION = "CUSTOM.ACTION"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -982,6 +1046,21 @@ public class EventTypes { entityEventDetails.put(EVENT_DISK_OFFERING_EDIT, DiskOffering.class); entityEventDetails.put(EVENT_DISK_OFFERING_DELETE, DiskOffering.class); + // GPU Cards + entityEventDetails.put(EVENT_GPU_CARD_CREATE, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_EDIT, GpuCard.class); + entityEventDetails.put(EVENT_GPU_CARD_DELETE, GpuCard.class); + + // vGPU Profiles + entityEventDetails.put(EVENT_VGPU_PROFILE_CREATE, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_EDIT, VgpuProfile.class); + entityEventDetails.put(EVENT_VGPU_PROFILE_DELETE, VgpuProfile.class); + + // GPU Devices + entityEventDetails.put(EVENT_GPU_DEVICE_CREATE, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_EDIT, GpuDevice.class); + entityEventDetails.put(EVENT_GPU_DEVICE_DELETE, GpuDevice.class); + // Network offerings entityEventDetails.put(EVENT_NETWORK_OFFERING_CREATE, NetworkOffering.class); entityEventDetails.put(EVENT_NETWORK_OFFERING_ASSIGN, NetworkOffering.class); @@ -1233,6 +1312,12 @@ public class EventTypes { entityEventDetails.put(EVENT_UPDATE_IMAGE_STORE_ACCESS_STATE, ImageStore.class); entityEventDetails.put(EVENT_LIVE_PATCH_SYSTEMVM, "SystemVMs"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_MAINTENANCE_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_PREPARE, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN_CANCEL, "ManagementServer"); + entityEventDetails.put(EVENT_MS_SHUTDOWN, "ManagementServer"); + //Object Store entityEventDetails.put(EVENT_OBJECT_STORE_CREATE, ObjectStore.class); entityEventDetails.put(EVENT_OBJECT_STORE_UPDATE, ObjectStore.class); @@ -1276,6 +1361,30 @@ public class EventTypes { entityEventDetails.put(EVENT_SHAREDFS_DESTROY, SharedFS.class); entityEventDetails.put(EVENT_SHAREDFS_EXPUNGE, SharedFS.class); entityEventDetails.put(EVENT_SHAREDFS_RECOVER, SharedFS.class); + + // Management Server + entityEventDetails.put(EVENT_MANAGEMENT_SERVER_REMOVE, "ManagementServer"); + + // VM Lease + entityEventDetails.put(VM_LEASE_EXPIRED, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_EXPIRING, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_DISABLED, VirtualMachine.class); + entityEventDetails.put(VM_LEASE_CANCELLED, VirtualMachine.class); + + // GUI theme + entityEventDetails.put(EVENT_GUI_THEME_CREATE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_REMOVE, "GuiTheme"); + entityEventDetails.put(EVENT_GUI_THEME_UPDATE, "GuiTheme"); + + // Extension + entityEventDetails.put(EVENT_EXTENSION_CREATE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_UPDATE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_DELETE, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_RESOURCE_REGISTER, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_RESOURCE_UNREGISTER, Extension.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_ADD, ExtensionCustomAction.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_UPDATE, ExtensionCustomAction.class); + entityEventDetails.put(EVENT_EXTENSION_CUSTOM_ACTION_DELETE, ExtensionCustomAction.class); } public static boolean isNetworkEvent(String eventType) { diff --git a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java index fe27408eb4e..66b607100d9 100644 --- a/api/src/main/java/com/cloud/exception/OperationTimedoutException.java +++ b/api/src/main/java/com/cloud/exception/OperationTimedoutException.java @@ -40,7 +40,7 @@ public class OperationTimedoutException extends CloudException { boolean _isActive; public OperationTimedoutException(Command[] cmds, long agentId, long seqId, int time, boolean isActive) { - super("Commands " + seqId + " to Host " + agentId + " timed out after " + time); + super("Commands " + seqId + " to Host " + agentId + " timed out after " + time + " secs"); _agentId = agentId; _seqId = seqId; _time = time; diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java index afac6df5631..07a0dfce041 100644 --- a/api/src/main/java/com/cloud/host/Host.java +++ b/api/src/main/java/com/cloud/host/Host.java @@ -53,9 +53,12 @@ public interface Host extends StateObject, Identity, Partition, HAResour return strs; } } - public static final String HOST_UEFI_ENABLE = "host.uefi.enable"; - public static final String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; - public static final String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + + String HOST_UEFI_ENABLE = "host.uefi.enable"; + String HOST_VOLUME_ENCRYPTION = "host.volume.encryption"; + String HOST_INSTANCE_CONVERSION = "host.instance.conversion"; + String HOST_OVFTOOL_VERSION = "host.ovftool.version"; + String HOST_VIRTV2V_VERSION = "host.virtv2v.version"; /** * @return name of the machine. @@ -213,4 +216,6 @@ public interface Host extends StateObject, Identity, Partition, HAResour ResourceState getResourceState(); CPU.CPUArch getArch(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java index 27ffef1c370..1f8741d3b7b 100644 --- a/api/src/main/java/com/cloud/hypervisor/Hypervisor.java +++ b/api/src/main/java/com/cloud/hypervisor/Hypervisor.java @@ -31,20 +31,22 @@ import java.util.stream.Collectors; import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.DirectDownloadTemplate; import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.RootDiskSizeOverride; import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigration; +import static com.cloud.hypervisor.Hypervisor.HypervisorType.Functionality.VmStorageMigrationWithSnapshots; public class Hypervisor { public static class HypervisorType { public enum Functionality { DirectDownloadTemplate, RootDiskSizeOverride, - VmStorageMigration + VmStorageMigration, + VmStorageMigrationWithSnapshots } private static final Map hypervisorTypeMap = new LinkedHashMap<>(); public static final HypervisorType None = new HypervisorType("None"); //for storage hosts public static final HypervisorType XenServer = new HypervisorType("XenServer", ImageFormat.VHD, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); public static final HypervisorType KVM = new HypervisorType("KVM", ImageFormat.QCOW2, EnumSet.of(DirectDownloadTemplate, RootDiskSizeOverride, VmStorageMigration)); - public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration)); + public static final HypervisorType VMware = new HypervisorType("VMware", ImageFormat.OVA, EnumSet.of(RootDiskSizeOverride, VmStorageMigration, VmStorageMigrationWithSnapshots)); public static final HypervisorType Hyperv = new HypervisorType("Hyperv"); public static final HypervisorType VirtualBox = new HypervisorType("VirtualBox"); public static final HypervisorType Parralels = new HypervisorType("Parralels"); @@ -54,6 +56,7 @@ public class Hypervisor { public static final HypervisorType Ovm3 = new HypervisorType("Ovm3", ImageFormat.RAW); public static final HypervisorType LXC = new HypervisorType("LXC"); public static final HypervisorType Custom = new HypervisorType("Custom", null, EnumSet.of(RootDiskSizeOverride)); + public static final HypervisorType External = new HypervisorType("External", null, EnumSet.of(RootDiskSizeOverride)); public static final HypervisorType Any = new HypervisorType("Any"); /*If you don't care about the hypervisor type*/ private final String name; private final ImageFormat imageFormat; diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java similarity index 78% rename from plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java rename to api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java index 591da077aec..571d993c7a1 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesCluster.java @@ -44,6 +44,8 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm AutoscaleRequested, ScaleUpRequested, ScaleDownRequested, + AddNodeRequested, + RemoveNodeRequested, UpgradeRequested, OperationSucceeded, OperationFailed, @@ -58,7 +60,10 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm Stopping("Resources for the Kubernetes cluster are being destroyed"), Stopped("All resources for the Kubernetes cluster are destroyed, Kubernetes cluster may still have ephemeral resource like persistent volumes provisioned"), Scaling("Transient state in which resources are either getting scaled up/down"), + ScalingStoppedCluster("Transient state in which the service offerings of stopped clusters are getting scaled"), Upgrading("Transient state in which cluster is getting upgraded"), + Importing("Transient state in which additional nodes are added as worker nodes to a cluster"), + RemovingNodes("Transient state in which additional nodes are removed from a cluster"), Alert("State to represent Kubernetes clusters which are not in expected desired state (operationally in active control place, stopped cluster VM's etc)."), Recovering("State in which Kubernetes cluster is recovering from alert state"), Destroyed("End state of Kubernetes cluster in which all resources are destroyed, cluster will not be usable further"), @@ -89,13 +94,27 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm s_fsm.addTransition(State.Running, Event.AutoscaleRequested, State.Scaling); s_fsm.addTransition(State.Running, Event.ScaleUpRequested, State.Scaling); s_fsm.addTransition(State.Running, Event.ScaleDownRequested, State.Scaling); + s_fsm.addTransition(State.Stopped, Event.ScaleUpRequested, State.ScalingStoppedCluster); s_fsm.addTransition(State.Scaling, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Scaling, Event.OperationFailed, State.Alert); + s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationSucceeded, State.Stopped); + s_fsm.addTransition(State.ScalingStoppedCluster, Event.OperationFailed, State.Alert); s_fsm.addTransition(State.Running, Event.UpgradeRequested, State.Upgrading); s_fsm.addTransition(State.Upgrading, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Upgrading, Event.OperationFailed, State.Alert); + s_fsm.addTransition(State.Running, Event.AddNodeRequested, State.Importing); + s_fsm.addTransition(State.Alert, Event.AddNodeRequested, State.Importing); + s_fsm.addTransition(State.Importing, Event.OperationSucceeded, State.Running); + s_fsm.addTransition(State.Importing, Event.OperationFailed, State.Running); + s_fsm.addTransition(State.Alert, Event.OperationSucceeded, State.Running); + + s_fsm.addTransition(State.Running, Event.RemoveNodeRequested, State.RemovingNodes); + s_fsm.addTransition(State.Alert, Event.RemoveNodeRequested, State.RemovingNodes); + s_fsm.addTransition(State.RemovingNodes, Event.OperationSucceeded, State.Running); + s_fsm.addTransition(State.RemovingNodes, Event.OperationFailed, State.Running); + s_fsm.addTransition(State.Alert, Event.RecoveryRequested, State.Recovering); s_fsm.addTransition(State.Recovering, Event.OperationSucceeded, State.Running); s_fsm.addTransition(State.Recovering, Event.OperationFailed, State.Alert); @@ -142,4 +161,13 @@ public interface KubernetesCluster extends ControlledEntity, com.cloud.utils.fsm Long getMaxSize(); Long getSecurityGroupId(); ClusterType getClusterType(); + Long getControlNodeServiceOfferingId(); + Long getWorkerNodeServiceOfferingId(); + Long getEtcdNodeServiceOfferingId(); + Long getControlNodeTemplateId(); + Long getWorkerNodeTemplateId(); + Long getEtcdNodeTemplateId(); + Long getEtcdNodeCount(); + Long getCniConfigId(); + String getCniConfigDetails(); } diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java index a13c1b3a6a8..37b8907b454 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesServiceHelper.java @@ -18,12 +18,23 @@ package com.cloud.kubernetes.cluster; import org.apache.cloudstack.acl.ControlledEntity; +import java.util.Map; + +import com.cloud.user.Account; import com.cloud.uservm.UserVm; import com.cloud.utils.component.Adapter; public interface KubernetesServiceHelper extends Adapter { + enum KubernetesClusterNodeType { + CONTROL, WORKER, ETCD, DEFAULT + } + ControlledEntity findByUuid(String uuid); ControlledEntity findByVmId(long vmId); void checkVmCanBeDestroyed(UserVm userVm); + boolean isValidNodeType(String nodeType); + Map getServiceOfferingNodeTypeMap(Map> serviceOfferingNodeTypeMap); + Map getTemplateNodeTypeMap(Map> templateNodeTypeMap); + void cleanupForAccount(Account account); } diff --git a/api/src/main/java/com/cloud/network/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java index ae1af450577..70d652b54e9 100644 --- a/api/src/main/java/com/cloud/network/IpAddress.java +++ b/api/src/main/java/com/cloud/network/IpAddress.java @@ -99,4 +99,5 @@ public interface IpAddress extends ControlledEntity, Identity, InternalIdentity, boolean isForSystemVms(); + boolean isForRouter(); } diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index d3bc5005cb7..dc94932e31f 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -206,6 +206,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Provider Tungsten = new Provider("Tungsten", false); public static final Provider Nsx = new Provider("Nsx", false); + public static final Provider Netris = new Provider("Netris", false); private final String name; private final boolean isExternal; diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index a4cd87af008..eb496ac4e0b 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -305,6 +305,8 @@ public interface NetworkModel { NicProfile getNicProfile(VirtualMachine vm, long networkId, String broadcastUri); + NicProfile getNicProfile(VirtualMachine vm, Nic nic, DataCenter dataCenter); + Set getAvailableIps(Network network, String requestedIp); String getDomainNetworkDomain(long domainId, long zoneId); diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index b8dd464b365..196e1f9aab8 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -19,7 +19,6 @@ package com.cloud.network; import java.util.List; import java.util.Map; -import com.cloud.dc.DataCenter; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; @@ -39,13 +38,16 @@ import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd; import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; +import com.cloud.agent.api.to.NicTO; +import com.cloud.dc.DataCenter; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network.IpAddresses; import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; @@ -57,7 +59,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; -import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; /** * The NetworkService interface is the "public" api to entities that make requests to the orchestration engine @@ -268,4 +269,10 @@ public interface NetworkService { InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId); InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId); List getInternalLoadBalancerElements(); + + boolean handleCksIsoOnNetworkVirtualRouter(Long virtualRouterId, boolean mount) throws ResourceUnavailableException; + + IpAddresses getIpAddressesFromIps(String ipAddress, String ip6Address, String macAddress); + + String getNicVlanValueForExternalVm(NicTO nic); } diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index dfa0ddb84ca..9f06a044111 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -129,7 +129,8 @@ public class Networks { UnDecided(null, null), OpenDaylight("opendaylight", String.class), TUNGSTEN("tf", String.class), - NSX("nsx", String.class); + NSX("nsx", String.class), + Netris("netris", String.class); private final String scheme; private final Class type; diff --git a/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java new file mode 100644 index 00000000000..a22db4287dc --- /dev/null +++ b/api/src/main/java/com/cloud/network/SDNProviderNetworkRule.java @@ -0,0 +1,358 @@ +// 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.network; + +import java.util.List; + +public class SDNProviderNetworkRule { + + protected long domainId; + protected long accountId; + protected long zoneId; + protected Long networkResourceId; + protected String networkResourceName; + protected boolean isVpcResource; + protected long vmId; + protected long ruleId; + protected String publicIp; + protected String vmIp; + protected String publicPort; + protected String privatePort; + protected String protocol; + protected String algorithm; + protected List sourceCidrList; + protected List destinationCidrList; + protected Integer icmpCode; + + protected Integer icmpType; + protected String trafficType; + protected Network.Service service; + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public void setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public void setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + } + + public boolean isVpcResource() { + return isVpcResource; + } + + public void setVpcResource(boolean vpcResource) { + isVpcResource = vpcResource; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public long getRuleId() { + return ruleId; + } + + public void setRuleId(long ruleId) { + this.ruleId = ruleId; + } + + public String getPublicIp() { + return publicIp; + } + + public void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + public String getVmIp() { + return vmIp; + } + + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } + + public String getPublicPort() { + return publicPort; + } + + public void setPublicPort(String publicPort) { + this.publicPort = publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(String privatePort) { + this.privatePort = privatePort; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getAlgorithm() { + return algorithm; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public List getSourceCidrList() { + return sourceCidrList; + } + + public void setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + } + + public List getDestinationCidrList() { + return destinationCidrList; + } + + public void setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + } + + public String getTrafficType() { + return trafficType; + } + + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } + + public static class Builder { + public long domainId; + public long accountId; + public long zoneId; + public Long networkResourceId; + public String networkResourceName; + public boolean isVpcResource; + public long vmId; + + public long ruleId; + public String publicIp; + public String vmIp; + public String publicPort; + public String privatePort; + public String protocol; + public String algorithm; + public List sourceCidrList; + public List destinationCidrList; + public String trafficType; + public Integer icmpType; + public Integer icmpCode; + public Network.Service service; + + public Builder() { + // Default constructor + } + + public Builder setDomainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder setAccountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + return this; + } + + public Builder setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + return this; + } + + public Builder setVpcResource(boolean isVpcResource) { + this.isVpcResource = isVpcResource; + return this; + } + + + public Builder setVmId(long vmId) { + this.vmId = vmId; + return this; + } + + public Builder setRuleId(long ruleId) { + this.ruleId = ruleId; + return this; + } + + public Builder setPublicIp(String publicIp) { + this.publicIp = publicIp; + return this; + } + + public Builder setVmIp(String vmIp) { + this.vmIp = vmIp; + return this; + } + + public Builder setPublicPort(String publicPort) { + this.publicPort = publicPort; + return this; + } + + public Builder setPrivatePort(String privatePort) { + this.privatePort = privatePort; + return this; + } + + public Builder setProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder setAlgorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder setTrafficType(String trafficType) { + this.trafficType = trafficType; + return this; + } + + public Builder setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + return this; + } + + public Builder setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + return this; + } + + public Builder setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + return this; + } + + public Builder setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + return this; + } + + public Builder setService(Network.Service service) { + this.service = service; + return this; + } + + public SDNProviderNetworkRule build() { + SDNProviderNetworkRule rule = new SDNProviderNetworkRule(); + rule.setDomainId(this.domainId); + rule.setAccountId(this.accountId); + rule.setZoneId(this.zoneId); + rule.setNetworkResourceId(this.networkResourceId); + rule.setNetworkResourceName(this.networkResourceName); + rule.setVpcResource(this.isVpcResource); + rule.setVmId(this.vmId); + rule.setVmIp(this.vmIp); + rule.setPublicIp(this.publicIp); + rule.setPublicPort(this.publicPort); + rule.setPrivatePort(this.privatePort); + rule.setProtocol(this.protocol); + rule.setRuleId(this.ruleId); + rule.setAlgorithm(this.algorithm); + rule.setIcmpType(this.icmpType); + rule.setIcmpCode(this.icmpCode); + rule.setSourceCidrList(this.sourceCidrList); + rule.setDestinationCidrList(this.destinationCidrList); + rule.setTrafficType(this.trafficType); + rule.setService(service); + return rule; + } + } +} diff --git a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java index 994df875f7d..51036abe060 100644 --- a/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java +++ b/api/src/main/java/com/cloud/network/Site2SiteVpnConnection.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.api.InternalIdentity; public interface Site2SiteVpnConnection extends ControlledEntity, InternalIdentity, Displayable { enum State { - Pending, Connecting, Connected, Disconnected, Error, + Pending, Connecting, Connected, Disconnected, Error, Removed } @Override diff --git a/api/src/main/java/com/cloud/network/element/NetworkElement.java b/api/src/main/java/com/cloud/network/element/NetworkElement.java index fa67575edd3..cb0fc2fca98 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkElement.java +++ b/api/src/main/java/com/cloud/network/element/NetworkElement.java @@ -23,6 +23,7 @@ import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.IpAddress; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; @@ -87,6 +88,14 @@ public interface NetworkElement extends Adapter { boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException; + /** + * Release IP from the network provider if reserved + * @param ipAddress + */ + default boolean releaseIp(IpAddress ipAddress) { + return true; + } + /** * The network is being shutdown. * @param network diff --git a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java index e99bc2fd416..8dcc8b6d0a4 100644 --- a/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/PortForwardingServiceProvider.java @@ -17,12 +17,40 @@ package com.cloud.network.element; import java.util.List; +import java.util.Objects; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.vpc.NetworkACLItem; public interface PortForwardingServiceProvider extends NetworkElement, IpDeployingRequester { + + static String getPublicPortRange(PortForwardingRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePFPortRange(PortForwardingRule rule) { + return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ? + String.valueOf(rule.getDestinationPortStart()) : + String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd())); + } + + static String getPrivatePortRange(FirewallRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + static String getPrivatePortRangeForACLRule(NetworkACLItem rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + /** * Apply rules * @param network diff --git a/api/src/main/java/com/cloud/network/element/VpcProvider.java b/api/src/main/java/com/cloud/network/element/VpcProvider.java index 6debd1fbc2d..fe8c8f8612f 100644 --- a/api/src/main/java/com/cloud/network/element/VpcProvider.java +++ b/api/src/main/java/com/cloud/network/element/VpcProvider.java @@ -55,4 +55,8 @@ public interface VpcProvider extends NetworkElement { boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List rules) throws ResourceUnavailableException; boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + default boolean updateVpc(Vpc vpc, String previousVpcName) { + return true; + } } diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java index 7b81c75ed84..ced664e54a9 100644 --- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java +++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java @@ -215,4 +215,8 @@ public interface NetworkGuru extends Adapter { default boolean isSlaacV6Only() { return true; } + + default boolean update(Network network, String prevNetworkName) { + return true; + } } diff --git a/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java b/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java index 46f17237e02..3fc6028b977 100644 --- a/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java +++ b/api/src/main/java/com/cloud/network/lb/LoadBalancingRulesService.java @@ -106,7 +106,7 @@ public interface LoadBalancingRulesService { boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException; - boolean assignCertToLoadBalancer(long lbRuleId, Long certId); + boolean assignCertToLoadBalancer(long lbRuleId, Long certId, boolean isForced); boolean removeCertFromLoadBalancer(long lbRuleId); diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClientException.java b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java similarity index 63% rename from vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClientException.java rename to api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java index 828eed4622d..afc21f7f511 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareClientException.java +++ b/api/src/main/java/com/cloud/network/netris/NetrisLbBackend.java @@ -14,20 +14,28 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.hypervisor.vmware.util; +package com.cloud.network.netris; -import com.cloud.exception.CloudException; +public class NetrisLbBackend { + private long vmId; + private String vmIp; + private int port; -public class VmwareClientException extends CloudException { - public VmwareClientException(String message, Throwable cause) { - super(message, cause); + public NetrisLbBackend(long vmId, String vmIp, int port) { + this.vmId = vmId; + this.vmIp = vmIp; + this.port = port; } - public VmwareClientException(String msg) { - super(msg); + public long getVmId() { + return vmId; } - // TODO embed vmware classes in this one for use downstream - public VmwareClientException(String msg, Exception embedded) { - super(msg, embedded); + + public String getVmIp() { + return vmIp; + } + + public int getPort() { + return port; } } diff --git a/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java new file mode 100644 index 00000000000..211517ead49 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisNetworkRule.java @@ -0,0 +1,108 @@ +// 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.network.netris; + +import com.cloud.network.SDNProviderNetworkRule; + + +import java.util.List; + +public class NetrisNetworkRule { + public enum NetrisRuleAction { + PERMIT, DENY + } + + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String lbRuleName; + private String lbCidrList; + private String reason; + + public NetrisNetworkRule(Builder builder) { + this.baseRule = builder.baseRule; + this.aclAction = builder.aclAction; + this.lbBackends = builder.lbBackends; + this.reason = builder.reason; + this.lbCidrList = builder.lbCidrList; + this.lbRuleName = builder.lbRuleName; + } + + public NetrisRuleAction getAclAction() { + return aclAction; + } + + public List getLbBackends() { + return lbBackends; + } + + public String getReason() { + return reason; + } + + public String getLbCidrList() {return lbCidrList; } + + public String getLbRuleName() { return lbRuleName; } + + public SDNProviderNetworkRule getBaseRule() { + return baseRule; + } + + // Builder class extending the parent builder + public static class Builder { + private SDNProviderNetworkRule baseRule; + private NetrisRuleAction aclAction; + private List lbBackends; + private String reason; + private String lbCidrList; + private String lbRuleName; + + public Builder baseRule(SDNProviderNetworkRule baseRule) { + this.baseRule = baseRule; + return this; + } + + public Builder aclAction(NetrisRuleAction aclAction) { + this.aclAction = aclAction; + return this; + } + + public Builder lbBackends(List lbBackends) { + this.lbBackends = lbBackends; + return this; + } + + public Builder reason(String reason) { + this.reason = reason; + return this; + } + + public Builder lbCidrList(String lbCidrList) { + this.lbCidrList = lbCidrList; + return this; + } + + public Builder lbRuleName(String lbRuleName) { + this.lbRuleName = lbRuleName; + return this; + } + + public NetrisNetworkRule build() { + return new NetrisNetworkRule(this); + } + } +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisProvider.java b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java new file mode 100644 index 00000000000..fccf2930e97 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisProvider.java @@ -0,0 +1,30 @@ +// 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.network.netris; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface NetrisProvider extends InternalIdentity, Identity { + long getZoneId(); + String getName(); + String getUrl(); + String getUsername(); + String getSiteName(); + String getTenantName(); + String getNetrisTag(); +} diff --git a/api/src/main/java/com/cloud/network/netris/NetrisService.java b/api/src/main/java/com/cloud/network/netris/NetrisService.java new file mode 100644 index 00000000000..110e9f07105 --- /dev/null +++ b/api/src/main/java/com/cloud/network/netris/NetrisService.java @@ -0,0 +1,310 @@ +// 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.network.netris; + +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.SDNProviderNetworkRule; +import com.cloud.network.vpc.StaticRoute; +import com.cloud.network.vpc.Vpc; + +import java.util.List; + +/** + * Interface for Netris Services that provides methods to manage VPCs, networks, + * NAT rules, network rules, and static routes in an SDN (Software Defined Networking) environment. + */ + +public interface NetrisService { + + /** + * Creates IPAM (IP Address Management) allocations for zone-level public ranges. + * + * @param zoneId the ID of the zone + * @return true if the operation is successful, false otherwise + */ + boolean createIPAMAllocationsForZoneLevelPublicRanges(long zoneId); + + /** + * Creates a VPC (Virtual Private Cloud) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the name of the VPC + * @param sourceNatEnabled true if source NAT is enabled + * @param cidr the CIDR of the VPC + * @param isVpcNetwork true if it is a VPC network + * @return true if the operation is successful, false otherwise + */ + boolean createVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled, String cidr, boolean isVpcNetwork); + + /** + * Updates an existing VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcId the ID of the VPC + * @param vpcName the new name of the VPC + * @param previousVpcName the previous name of the VPC + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcResource(long zoneId, long accountId, long domainId, Long vpcId, String vpcName, String previousVpcName); + + /** + * Deletes a VPC resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpc the VPC to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteVpcResource(long zoneId, long accountId, long domainId, Vpc vpc); + + /** + * Creates a virtual network (vNet) resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @param globalRouting true if global routing is enabled + * @return true if the operation is successful, false otherwise + */ + boolean createVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr, Boolean globalRouting); + + /** + * Updates an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the new name of the network + * @param networkId the ID of the network + * @param prevNetworkName the previous name of the network + * @return true if the operation is successful, false otherwise + */ + boolean updateVnetResource(Long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String prevNetworkName); + + /** + * Deletes an existing vNet resource. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param cidr the CIDR of the network + * @return true if the operation is successful, false otherwise + */ + boolean deleteVnetResource(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, String cidr); + + /** + * Creates a source NAT rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param sourceNatIp the source NAT IP + * @return true if the operation is successful, false otherwise + */ + boolean createSnatRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, long networkId, boolean isForVpc, String vpcCidr, String sourceNatIp); + + /** + * Creates a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to forward + * @return true if the operation is successful, false otherwise + */ + boolean createPortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Deletes a port forwarding rule for a VPC or network. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param vpcName the name of the VPC + * @param vpcId the ID of the VPC + * @param networkName the name of the network + * @param networkId the ID of the network + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param networkRule the network rule to remove + * @return true if the operation is successful, false otherwise + */ + boolean deletePortForwardingRule(long zoneId, long accountId, long domainId, String vpcName, Long vpcId, String networkName, Long networkId, boolean isForVpc, String vpcCidr, SDNProviderNetworkRule networkRule); + + /** + * Updates the source NAT IP for a specified VPC. + * + * @param vpc the VPC to updates + * @param address the new source NAT IP address + * @return true if the operation is successful, false otherwise + */ + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + + /** + * Creates a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param vpcCidr the VPC CIDR + * @param staticNatIp the static NAT IP + * @param vmIp the VM's IP address + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean createStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String vpcCidr, String staticNatIp, String vmIp, long vmId); + + /** + * Deletes a static NAT rule for a specific VM. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if the rule applies to a VPC + * @param staticNatIp the static NAT IP + * @param vmId the ID of the VM + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticNatRule(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String staticNatIp, long vmId); + + /** + * Adds firewall rules to a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to add + * @return true if the operation is successful, false otherwise + */ + boolean addFirewallRules(Network network, List firewallRules); + + /** + * Deletes firewall rules from a specific network. + * + * @param network the target network + * @param firewallRules the list of firewall rules to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteFirewallRules(Network network, List firewallRules); + + /** + * Adds or updates a static route for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @param updateRoute true if the route should be updated + * @return true if the operation is successful, false otherwise + */ + boolean addOrUpdateStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId, boolean updateRoute); + + /** + * Deletes a specific static route for a network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return true if the operation is successful, false otherwise + */ + boolean deleteStaticRoute(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Lists static routes for a specific network or VPC. + * + * @param zoneId the ID of the zone + * @param accountId the ID of the account + * @param domainId the ID of the domain + * @param networkResourceName the name of the network resource + * @param networkResourceId the ID of the network resource + * @param isForVpc true if it is for a VPC + * @param prefix the IP prefix of the route + * @param nextHop the next hop address + * @param routeId the ID of the route + * @return a list of static routes + */ + List listStaticRoutes(long zoneId, long accountId, long domainId, String networkResourceName, Long networkResourceId, boolean isForVpc, String prefix, String nextHop, Long routeId); + + /** + * Releases a NAT IP address. + * + * @param zoneId the ID of the zone + * @param publicIp the public NAT IP to release + * @return true if the operation is successful, false otherwise + */ + boolean releaseNatIp(long zoneId, String publicIp); + + /** + * Creates or updates a load balancer (LB) rule. + * + * @param rule the network rule for the load balancer + * @return true if the operation is successful, false otherwise + */ + boolean createOrUpdateLbRule(NetrisNetworkRule rule); + + /** + * Deletes a load balancer (LB) rule. + * + * @param rule the network rule to delete + * @return true if the operation is successful, false otherwise + */ + boolean deleteLbRule(NetrisNetworkRule rule); +} diff --git a/api/src/main/java/com/cloud/network/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java index bc4e6aafbfe..1adb7461cc0 100644 --- a/api/src/main/java/com/cloud/network/nsx/NsxService.java +++ b/api/src/main/java/com/cloud/network/nsx/NsxService.java @@ -16,9 +16,10 @@ // under the License. package com.cloud.network.nsx; +import org.apache.cloudstack.framework.config.ConfigKey; + import com.cloud.network.IpAddress; import com.cloud.network.vpc.Vpc; -import org.apache.cloudstack.framework.config.ConfigKey; public interface NsxService { @@ -33,4 +34,5 @@ public interface NsxService { boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled); boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); + String getSegmentId(long domainId, long accountId, long zoneId, Long vpcId, long networkId); } diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java index 5707ca14024..739fca328b8 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRoute.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRoute.java @@ -25,6 +25,7 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit Staged, // route been created but has never got through network rule conflict detection. Routes in this state can not be sent to VPC virtual router. Add, // Add means the route has been created and has gone through network rule conflict detection. Active, // Route has been sent to the VPC router and reported to be active. + Update, Revoke, // Revoke means this route has been revoked. If this route has been sent to the VPC router, the route will be deleted from database. Deleting // rule has been revoked and is scheduled for deletion } @@ -32,7 +33,9 @@ public interface StaticRoute extends ControlledEntity, Identity, InternalIdentit /** * @return */ - long getVpcGatewayId(); + Long getVpcGatewayId(); + + String getNextHop(); /** * @return diff --git a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java index cb4849f1f7b..c8fc073911f 100644 --- a/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java +++ b/api/src/main/java/com/cloud/network/vpc/StaticRouteProfile.java @@ -23,7 +23,8 @@ public class StaticRouteProfile implements StaticRoute { private String targetCidr; private long accountId; private long domainId; - private long gatewayId; + private Long gatewayId; + private String nextHop; private StaticRoute.State state; private long vpcId; String vlanTag; @@ -46,6 +47,18 @@ public class StaticRouteProfile implements StaticRoute { ipAddress = gateway.getIp4Address(); } + public StaticRouteProfile(StaticRoute staticRoute) { + id = staticRoute.getId(); + uuid = staticRoute.getUuid(); + targetCidr = staticRoute.getCidr(); + accountId = staticRoute.getAccountId(); + domainId = staticRoute.getDomainId(); + gatewayId = staticRoute.getVpcGatewayId(); + state = staticRoute.getState(); + vpcId = staticRoute.getVpcId(); + gateway = staticRoute.getNextHop(); + } + @Override public long getAccountId() { return accountId; @@ -57,10 +70,15 @@ public class StaticRouteProfile implements StaticRoute { } @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return gatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return targetCidr; diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java b/api/src/main/java/com/cloud/network/vpc/Vpc.java index e9a831c9d83..b94089d2d43 100644 --- a/api/src/main/java/com/cloud/network/vpc/Vpc.java +++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java @@ -105,4 +105,6 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity { String getIp6Dns1(); String getIp6Dns2(); + + boolean useRouterIpAsResolver(); } diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index 38263f59667..17f49bb3652 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -32,6 +32,8 @@ public interface VpcOffering extends InternalIdentity, Identity { public static final String redundantVPCOfferingName = "Redundant VPC offering"; public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode"; public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode"; + public static final String DEFAULT_VPC_ROUTE_NETRIS_OFFERING_NAME = "VPC offering with Netris - Route Mode"; + public static final String DEFAULT_VPC_NAT_NETRIS_OFFERING_NAME = "VPC offering with Netris - NAT Mode"; /** * @@ -56,8 +58,6 @@ public interface VpcOffering extends InternalIdentity, Identity { */ boolean isDefault(); - boolean isForNsx(); - NetworkOffering.NetworkMode getNetworkMode(); /** diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java index 10f1ddcc12d..97b95339ecf 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -37,7 +37,7 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, - Long serviceOfferingId, Boolean forNsx, NetworkOffering.NetworkMode networkMode, + Long serviceOfferingId, String externalProvider, NetworkOffering.NetworkMode networkMode, List domainIds, List zoneIds, VpcOffering.State state, NetworkOffering.RoutingMode routingMode, boolean specifyAsNumber); diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java b/api/src/main/java/com/cloud/network/vpc/VpcService.java index af2a9847a62..c1546609d2b 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -48,17 +48,17 @@ public interface VpcService { * @param vpcName * @param displayText * @param cidr - * @param networkDomain TODO + * @param networkDomain TODO * @param ip4Dns1 * @param ip4Dns2 - * @param displayVpc TODO + * @param displayVpc TODO + * @param useVrIpResolver * @return * @throws ResourceAllocationException TODO */ Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize, - Long asNumber, List bgpPeerIds) - throws ResourceAllocationException; + Long asNumber, List bgpPeerIds, Boolean useVrIpResolver) throws ResourceAllocationException; /** * Persists VPC record in the database @@ -238,7 +238,7 @@ public interface VpcService { * @param cidr * @return */ - StaticRoute createStaticRoute(long gatewayId, String cidr) throws NetworkRuleConflictException; + StaticRoute createStaticRoute(Long gatewayId, Long vpcId, String nextHop, String cidr) throws NetworkRuleConflictException; /** * Lists static routes based on parameters passed to the call diff --git a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java index d83039e15c2..12dcf423e34 100644 --- a/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java +++ b/api/src/main/java/com/cloud/offering/DiskOfferingInfo.java @@ -31,6 +31,13 @@ public class DiskOfferingInfo { _diskOffering = diskOffering; } + public DiskOfferingInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + _diskOffering = diskOffering; + _size = size; + _minIops = minIops; + _maxIops = maxIops; + } + public void setDiskOffering(DiskOffering diskOffering) { _diskOffering = diskOffering; } diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index 7011aea679e..5000a4f8c62 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -64,6 +64,8 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC = "DefaultNATNSXNetworkOfferingForVpc"; public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB"; public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc"; + public static final String DEFAULT_ROUTED_NETRIS_OFFERING_FOR_VPC = "DefaultRoutedNetrisNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NETRIS_OFFERING_FOR_VPC = "DefaultNATNetrisNetworkOfferingForVpc"; public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering"; public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering"; public final static String QuickCloudNoServices = "QuickCloudNoServices"; @@ -102,10 +104,6 @@ public interface NetworkOffering extends InfrastructureEntity, InternalIdentity, boolean isForVpc(); - boolean isForTungsten(); - - boolean isForNsx(); - NetworkMode getNetworkMode(); TrafficType getTrafficType(); diff --git a/api/src/main/java/com/cloud/offering/ServiceOffering.java b/api/src/main/java/com/cloud/offering/ServiceOffering.java index acb7a9f1cf9..532123e4373 100644 --- a/api/src/main/java/com/cloud/offering/ServiceOffering.java +++ b/api/src/main/java/com/cloud/offering/ServiceOffering.java @@ -142,4 +142,8 @@ public interface ServiceOffering extends InfrastructureEntity, InternalIdentity, Boolean getDiskOfferingStrictness(); void setDiskOfferingStrictness(boolean diskOfferingStrictness); + + Long getVgpuProfileId(); + + Integer getGpuCount(); } diff --git a/api/src/main/java/com/cloud/org/Cluster.java b/api/src/main/java/com/cloud/org/Cluster.java index 5124168084c..b0aa6bb04cf 100644 --- a/api/src/main/java/com/cloud/org/Cluster.java +++ b/api/src/main/java/com/cloud/org/Cluster.java @@ -41,4 +41,6 @@ public interface Cluster extends Grouping, Partition { ManagedState getManagedState(); CPU.CPUArch getArch(); + + String getStorageAccessGroups(); } diff --git a/api/src/main/java/com/cloud/resource/ResourceService.java b/api/src/main/java/com/cloud/resource/ResourceService.java index 562c3c418df..3cdf8fc64e9 100644 --- a/api/src/main/java/com/cloud/resource/ResourceService.java +++ b/api/src/main/java/com/cloud/resource/ResourceService.java @@ -95,4 +95,11 @@ public interface ResourceService { boolean releaseHostReservation(Long hostId); + void updatePodStorageAccessGroups(long podId, List newStorageAccessGroups); + + void updateZoneStorageAccessGroups(long zoneId, List newStorageAccessGroups); + + void updateClusterStorageAccessGroups(Long clusterId, List newStorageAccessGroups); + + void updateHostStorageAccessGroups(Long hostId, List newStorageAccessGroups); } diff --git a/api/src/main/java/com/cloud/resource/ResourceState.java b/api/src/main/java/com/cloud/resource/ResourceState.java index 70738c7921b..e91cf820b08 100644 --- a/api/src/main/java/com/cloud/resource/ResourceState.java +++ b/api/src/main/java/com/cloud/resource/ResourceState.java @@ -76,6 +76,10 @@ public enum ResourceState { } } + public static List s_maintenanceStates = List.of(ResourceState.Maintenance, + ResourceState.ErrorInMaintenance, ResourceState.PrepareForMaintenance, + ResourceState.ErrorInPrepareForMaintenance); + public ResourceState getNextState(Event a) { return s_fsm.getNextState(this, a); } @@ -98,8 +102,7 @@ public enum ResourceState { } public static boolean isMaintenanceState(ResourceState state) { - return Arrays.asList(ResourceState.Maintenance, ResourceState.ErrorInMaintenance, - ResourceState.PrepareForMaintenance, ResourceState.ErrorInPrepareForMaintenance).contains(state); + return s_maintenanceStates.contains(state); } public static boolean canAttemptMaintenance(ResourceState state) { diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index 18f3e901cd9..f7b2456ce5c 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -25,16 +25,20 @@ import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgGroupsByCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.DeleteGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.GetHypervisorGuestOsNamesCmd; import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCategoryCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd; import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; +import org.apache.cloudstack.api.command.admin.management.RemoveManagementServerCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; @@ -59,8 +63,10 @@ import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.userdata.DeleteCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.DeleteUserDataCmd; import org.apache.cloudstack.api.command.user.userdata.ListUserDataCmd; +import org.apache.cloudstack.api.command.user.userdata.RegisterCniConfigurationCmd; import org.apache.cloudstack.api.command.user.userdata.RegisterUserDataCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; @@ -168,6 +174,12 @@ public interface ManagementService { */ Pair, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd); + GuestOsCategory addGuestOsCategory(AddGuestOsCategoryCmd cmd); + + GuestOsCategory updateGuestOsCategory(UpdateGuestOsCategoryCmd cmd); + + boolean deleteGuestOsCategory(DeleteGuestOsCategoryCmd cmd); + /** * Obtains a list of all guest OS mappings * @@ -360,17 +372,23 @@ public interface ManagementService { * The api command class. * @return The list of userdatas found. */ - Pair, Integer> listUserDatas(ListUserDataCmd cmd); + Pair, Integer> listUserDatas(ListUserDataCmd cmd, boolean forCks); + + /** + * Registers a cni configuration. + * + * @param cmd The api command class. + * @return A VO with the registered user data. + */ + UserData registerCniConfiguration(RegisterCniConfigurationCmd cmd); /** * Registers a userdata. * - * @param cmd - * The api command class. + * @param cmd The api command class. * @return A VO with the registered userdata. */ UserData registerUserData(RegisterUserDataCmd cmd); - /** * Deletes a userdata. * @@ -380,6 +398,14 @@ public interface ManagementService { */ boolean deleteUserData(DeleteUserDataCmd cmd); + /** + * Deletes user data. + * + * @param cmd + * The api command class. + * @return True on success. False otherwise. + */ + boolean deleteCniConfiguration(DeleteCniConfigurationCmd cmd); /** * Search registered key pairs for the logged in user. * @@ -481,4 +507,6 @@ public interface ManagementService { Pair patchSystemVM(PatchSystemVMCmd cmd); + boolean removeManagementServer(RemoveManagementServerCmd cmd); + } diff --git a/api/src/main/java/com/cloud/server/ResourceIconManager.java b/api/src/main/java/com/cloud/server/ResourceIconManager.java index e5111d9160b..d10b3eb0cd5 100644 --- a/api/src/main/java/com/cloud/server/ResourceIconManager.java +++ b/api/src/main/java/com/cloud/server/ResourceIconManager.java @@ -16,7 +16,9 @@ // under the License. package com.cloud.server; +import java.util.Collection; import java.util.List; +import java.util.Map; public interface ResourceIconManager { @@ -25,4 +27,8 @@ public interface ResourceIconManager { boolean deleteResourceIcon(List resourceIds, ResourceTag.ResourceObjectType resourceType); ResourceIcon getByResourceTypeAndUuid(ResourceTag.ResourceObjectType type, String resourceId); + + Map getByResourceTypeAndIds(ResourceTag.ResourceObjectType type, Collection resourceIds); + + Map getByResourceTypeAndUuids(ResourceTag.ResourceObjectType type, Collection resourceUuids); } diff --git a/api/src/main/java/com/cloud/server/ResourceTag.java b/api/src/main/java/com/cloud/server/ResourceTag.java index 9bbb5d43eae..b3026deceff 100644 --- a/api/src/main/java/com/cloud/server/ResourceTag.java +++ b/api/src/main/java/com/cloud/server/ResourceTag.java @@ -66,6 +66,7 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit LBStickinessPolicy(false, true), LBHealthCheckPolicy(false, true), SnapshotPolicy(true, true), + GuestOsCategory(false, false, true), GuestOs(false, true), NetworkOffering(false, true), VpcOffering(true, false), diff --git a/api/src/main/java/com/cloud/storage/GuestOsCategory.java b/api/src/main/java/com/cloud/storage/GuestOsCategory.java index b46418d5c8f..e1ee4489158 100644 --- a/api/src/main/java/com/cloud/storage/GuestOsCategory.java +++ b/api/src/main/java/com/cloud/storage/GuestOsCategory.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import java.util.Date; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -27,4 +29,7 @@ public interface GuestOsCategory extends Identity, InternalIdentity { void setName(String name); + boolean isFeatured(); + + Date getCreated(); } diff --git a/api/src/main/java/com/cloud/storage/MigrationOptions.java b/api/src/main/java/com/cloud/storage/MigrationOptions.java index a39a2a7c827..8b642d09e29 100644 --- a/api/src/main/java/com/cloud/storage/MigrationOptions.java +++ b/api/src/main/java/com/cloud/storage/MigrationOptions.java @@ -24,6 +24,7 @@ public class MigrationOptions implements Serializable { private String srcPoolUuid; private Storage.StoragePoolType srcPoolType; + private Long srcPoolClusterId; private Type type; private ScopeType scopeType; private String srcBackingFilePath; @@ -38,21 +39,23 @@ public class MigrationOptions implements Serializable { public MigrationOptions() { } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.LinkedClone; this.scopeType = scopeType; this.srcBackingFilePath = srcBackingFilePath; this.copySrcTemplate = copySrcTemplate; + this.srcPoolClusterId = srcPoolClusterId; } - public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType) { + public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType, Long srcPoolClusterId) { this.srcPoolUuid = srcPoolUuid; this.srcPoolType = srcPoolType; this.type = Type.FullClone; this.scopeType = scopeType; this.srcVolumeUuid = srcVolumeUuid; + this.srcPoolClusterId = srcPoolClusterId; } public String getSrcPoolUuid() { @@ -63,6 +66,10 @@ public class MigrationOptions implements Serializable { return srcPoolType; } + public Long getSrcPoolClusterId() { + return srcPoolClusterId; + } + public ScopeType getScopeType() { return scopeType; } public String getSrcBackingFilePath() { diff --git a/api/src/main/java/com/cloud/storage/Snapshot.java b/api/src/main/java/com/cloud/storage/Snapshot.java index fc919e442b2..c0a7b812ed9 100644 --- a/api/src/main/java/com/cloud/storage/Snapshot.java +++ b/api/src/main/java/com/cloud/storage/Snapshot.java @@ -48,7 +48,7 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, } public enum State { - Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, + Allocated, Creating, CreatedOnPrimary, BackingUp, BackedUp, Copying, Destroying, Destroyed, Hidden, //it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage Error; diff --git a/api/src/main/java/com/cloud/storage/Storage.java b/api/src/main/java/com/cloud/storage/Storage.java index 05b8b3ab7a8..1ad3731b9ea 100644 --- a/api/src/main/java/com/cloud/storage/Storage.java +++ b/api/src/main/java/com/cloud/storage/Storage.java @@ -30,6 +30,7 @@ public class Storage { OVA(true, true, true, "ova"), VHDX(true, true, true, "vhdx"), BAREMETAL(false, false, false, "BAREMETAL"), + EXTERNAL(false, false, false, "EXTERNAL"), VMDK(true, true, false, "vmdk"), VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"), diff --git a/api/src/main/java/com/cloud/storage/StorageService.java b/api/src/main/java/com/cloud/storage/StorageService.java index b8df75cd3e4..a29c8f6aece 100644 --- a/api/src/main/java/com/cloud/storage/StorageService.java +++ b/api/src/main/java/com/cloud/storage/StorageService.java @@ -22,6 +22,7 @@ import java.util.Map; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; import org.apache.cloudstack.api.command.admin.storage.ChangeStoragePoolScopeCmd; +import org.apache.cloudstack.api.command.admin.storage.ConfigureStorageAccessCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd; @@ -99,6 +100,8 @@ public interface StorageService { StoragePool disablePrimaryStoragePool(Long id); + boolean configureStorageAccess(ConfigureStorageAccessCmd cmd); + StoragePool getStoragePool(long id); boolean deleteImageStore(DeleteImageStoreCmd cmd); @@ -131,7 +134,7 @@ public interface StorageService { void removeSecondaryStorageHeuristic(RemoveSecondaryStorageSelectorCmd cmd); - ObjectStore discoverObjectStore(String name, String url, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; + ObjectStore discoverObjectStore(String name, String url, Long size, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; boolean deleteObjectStore(DeleteObjectStoragePoolCmd cmd); diff --git a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java index f43d5331222..db702a61f2b 100644 --- a/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java +++ b/api/src/main/java/com/cloud/storage/VMTemplateStorageResourceAssoc.java @@ -17,6 +17,7 @@ package com.cloud.storage; import java.util.Date; +import java.util.List; import org.apache.cloudstack.api.InternalIdentity; @@ -25,6 +26,8 @@ public interface VMTemplateStorageResourceAssoc extends InternalIdentity { UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED } + List PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS); + String getInstallPath(); long getTemplateId(); diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index 3a72f8bddc4..4140d51a800 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -22,7 +22,12 @@ import java.net.MalformedURLException; import java.util.List; import java.util.Map; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.offering.DiskOffering; +import com.cloud.user.Account; import com.cloud.utils.Pair; +import com.cloud.utils.fsm.NoTransitionException; + import org.apache.cloudstack.api.command.user.volume.AssignVolumeCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; @@ -37,13 +42,9 @@ import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.framework.config.ConfigKey; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.user.Account; -import com.cloud.utils.fsm.NoTransitionException; - public interface VolumeApiService { - ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey("Advanced" + ConfigKey ConcurrentMigrationsThresholdPerDatastore = new ConfigKey<>("Advanced" , Long.class , "concurrent.migrations.per.target.datastore" , "0" @@ -51,7 +52,7 @@ public interface VolumeApiService { , true // not sure if this is to be dynamic , ConfigKey.Scope.Global); - ConfigKey UseHttpsToUpload = new ConfigKey("Advanced", + ConfigKey UseHttpsToUpload = new ConfigKey<>("Advanced", Boolean.class, "use.https.to.upload", "true", @@ -85,7 +86,7 @@ public interface VolumeApiService { * @param cmd * the API command wrapping the criteria * @return the volume object - * @throws ResourceAllocationException + * @throws ResourceAllocationException no capacity to allocate the new volume size */ Volume resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException; @@ -112,10 +113,10 @@ public interface VolumeApiService { Volume detachVolumeFromVM(DetachVolumeCmd cmd); - Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds) + Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup, Map tags, List zoneIds, List poolIds, Boolean useStorageReplication) throws ResourceAllocationException; - Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds) throws ResourceAllocationException; + Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType, List zoneIds, List storagePoolIds, Boolean useStorageReplication) throws ResourceAllocationException; Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, Boolean deleteProtection, @@ -136,16 +137,16 @@ public interface VolumeApiService { void updateDisplay(Volume volume, Boolean displayVolume); - Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName) throws ResourceAllocationException; + Snapshot allocSnapshotForVm(Long vmId, Long volumeId, String snapshotName, Long vmSnapshotId) throws ResourceAllocationException; /** - * Checks if the target storage supports the disk offering. + * Checks if the storage pool supports the disk offering tags. * This validation is consistent with the mechanism used to select a storage pool to deploy a volume when a virtual machine is deployed or when a data disk is allocated. * * The scenarios when this method returns true or false is presented in the following table. * * - * + * * * * @@ -169,7 +170,15 @@ public interface VolumeApiService { * *
#Disk offering tagsStorage tagsDoes the storage support the disk offering?#Disk offering diskOfferingTagsStorage diskOfferingTagsDoes the storage support the disk offering?
*/ - boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String diskOfferingTags); + boolean doesStoragePoolSupportDiskOffering(StoragePool destPool, DiskOffering diskOffering); + + /** + * Checks if the storage pool supports the required disk offering tags + * destPool the storage pool to check the disk offering tags + * diskOfferingTags the tags that should be supported + * return whether the tags are supported in the storage pool + */ + boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags); Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge); diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index 5b494c308c3..6138f24c92b 100644 --- a/api/src/main/java/com/cloud/template/TemplateApiService.java +++ b/api/src/main/java/com/cloud/template/TemplateApiService.java @@ -58,10 +58,23 @@ public interface TemplateApiService { VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId); + /** + * Detach ISO from VM + * @param vmId id of the VM + * @param isoId id of the ISO (when passed). If it is not passed, it will get it from user_vm table + * @param extraParams forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean detachIso(long vmId, Long isoId, Boolean... extraParams); - boolean detachIso(long vmId, boolean forced); - - boolean attachIso(long isoId, long vmId, boolean forced); + /** + * Attach ISO to a VM + * @param isoId id of the ISO to attach + * @param vmId id of the VM to attach the ISO to + * @param extraParams: forced, isVirtualRouter + * @return true when operation succeeds, false if not + */ + boolean attachIso(long isoId, long vmId, Boolean... extraParams); /** * Deletes a template diff --git a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java index d8872d5fe72..b8c646048b9 100644 --- a/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java +++ b/api/src/main/java/com/cloud/template/VirtualMachineTemplate.java @@ -145,10 +145,14 @@ public interface VirtualMachineTemplate extends ControlledEntity, Identity, Inte boolean isDeployAsIs(); + boolean isForCks(); + Long getUserDataId(); UserData.UserDataOverridePolicy getUserDataOverridePolicy(); CPU.CPUArch getArch(); + Long getExtensionId(); + } diff --git a/api/src/main/java/com/cloud/user/Account.java b/api/src/main/java/com/cloud/user/Account.java index 6be4d0a48f6..1cc5eae64a6 100644 --- a/api/src/main/java/com/cloud/user/Account.java +++ b/api/src/main/java/com/cloud/user/Account.java @@ -71,6 +71,7 @@ public interface Account extends ControlledEntity, InternalIdentity, Identity { } public static final long ACCOUNT_ID_SYSTEM = 1; + public static final long ACCOUNT_ID_ADMIN = 2; public String getAccountName(); diff --git a/api/src/main/java/com/cloud/user/AccountService.java b/api/src/main/java/com/cloud/user/AccountService.java index e2c3bed0c29..c0ebcf09f59 100644 --- a/api/src/main/java/com/cloud/user/AccountService.java +++ b/api/src/main/java/com/cloud/user/AccountService.java @@ -87,6 +87,8 @@ public interface AccountService { boolean isDomainAdmin(Long accountId); + boolean isResourceDomainAdmin(Long accountId); + boolean isNormalUser(long accountId); User getActiveUserByRegistrationToken(String registrationToken); diff --git a/api/src/main/java/com/cloud/user/ResourceLimitService.java b/api/src/main/java/com/cloud/user/ResourceLimitService.java index 2f4ad1347be..49b20fe2fef 100644 --- a/api/src/main/java/com/cloud/user/ResourceLimitService.java +++ b/api/src/main/java/com/cloud/user/ResourceLimitService.java @@ -50,8 +50,14 @@ public interface ResourceLimitService { "The default maximum number of projects that can be created for an account",false); static final ConfigKey DefaultMaxDomainProjects = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.projects","50", "The default maximum number of projects that can be created for a domain",false); + static final ConfigKey DefaultMaxAccountGpus = new ConfigKey<>("Account Defaults",Long.class,"max.account.gpus","20", + "The default maximum number of GPU devices that can be used for an account", false); + static final ConfigKey DefaultMaxDomainGpus = new ConfigKey<>("Domain Defaults",Long.class,"max.domain.gpus","20", + "The default maximum number of GPU devices that can be used for a domain", false); + static final ConfigKey DefaultMaxProjectGpus = new ConfigKey<>("Project Defaults",Long.class,"max.project.gpus","20", + "The default maximum number of GPU devices that can be used for a project", false); - static final List HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory); + static final List HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory, ResourceType.gpu); static final List StorageTagsSupportingTypes = List.of(ResourceType.volume, ResourceType.primary_storage); /** @@ -284,4 +290,8 @@ public interface ResourceLimitService { void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory); + void checkVmGpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu) throws ResourceAllocationException; + void incrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu); + void decrementVmGpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long gpu); + } diff --git a/api/src/main/java/com/cloud/user/UserData.java b/api/src/main/java/com/cloud/user/UserData.java index fa0c50473c0..13a3c74f367 100644 --- a/api/src/main/java/com/cloud/user/UserData.java +++ b/api/src/main/java/com/cloud/user/UserData.java @@ -29,4 +29,5 @@ public interface UserData extends ControlledEntity, InternalIdentity, Identity { String getUserData(); String getParams(); + boolean isForCks(); } diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index 72b18b70e18..6f1aba4613d 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -16,14 +16,18 @@ // under the License. package com.cloud.vm; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import com.cloud.deploy.DeploymentPlan; import org.apache.cloudstack.api.BaseCmd.HTTPMethod; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; import org.apache.cloudstack.api.command.user.vm.RebootVMCmd; @@ -111,7 +115,7 @@ public interface UserVmService { UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException; - void startVirtualMachine(UserVm vm) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException; + void startVirtualMachine(UserVm vm, DeploymentPlan plan) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException; void startVirtualMachineForHA(VirtualMachine vm, Map params, DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException, @@ -217,11 +221,11 @@ public interface UserVmService { * available. */ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, - Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameter, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -294,10 +298,10 @@ public interface UserVmService { * available. */ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is disabled) @@ -366,10 +370,10 @@ public interface UserVmService { * available. */ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, - String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, + String hostName, String displayName, Long diskOfferingId, Long diskSize, List dataDiskInfoList, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List sshKeyPairs, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; @@ -513,4 +517,8 @@ public interface UserVmService { * @return true if the VM is successfully unmanaged, false if not. */ boolean unmanageUserVM(Long vmId); + + UserVm allocateVMFromBackup(CreateVMFromBackupCmd cmd) throws InsufficientCapacityException, ResourceAllocationException, ResourceUnavailableException; + + UserVm restoreVMFromBackup(CreateVMFromBackupCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index e2ea408e7b8..d244de7115e 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -128,7 +128,6 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Partition, s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging, null)); s_fsm.addTransition(new Transition(State.Stopped, Event.RestoringRequested, State.Restoring, null)); - s_fsm.addTransition(new Transition(State.Expunging, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Destroyed, Event.RestoringRequested, State.Restoring, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringSuccess, State.Stopped, null)); s_fsm.addTransition(new Transition(State.Restoring, Event.RestoringFailed, State.Stopped, null)); diff --git a/api/src/main/java/com/cloud/vm/VmDetailConstants.java b/api/src/main/java/com/cloud/vm/VmDetailConstants.java index 29803d5271b..596c861218f 100644 --- a/api/src/main/java/com/cloud/vm/VmDetailConstants.java +++ b/api/src/main/java/com/cloud/vm/VmDetailConstants.java @@ -41,6 +41,7 @@ public interface VmDetailConstants { String KVM_VNC_PORT = "kvm.vnc.port"; String KVM_VNC_ADDRESS = "kvm.vnc.address"; String KVM_VNC_PASSWORD = "kvm.vnc.password"; + String KVM_GUEST_OS_MACHINE_TYPE = "kvm.guest.os.machine.type"; // KVM specific, custom virtual GPU hardware String VIDEO_HARDWARE = "video.hardware"; @@ -89,6 +90,9 @@ public interface VmDetailConstants { String DEPLOY_AS_IS_CONFIGURATION = "configurationId"; String KEY_PAIR_NAMES = "keypairnames"; String CKS_CONTROL_NODE_LOGIN_USER = "controlNodeLoginUser"; + String CKS_NODE_TYPE = "node"; + String OFFERING = "offering"; + String TEMPLATE = "template"; // VMware to KVM VM migrations specific String VMWARE_TO_KVM_PREFIX = "vmware-to-kvm"; @@ -101,4 +105,25 @@ public interface VmDetailConstants { String VMWARE_HOST_NAME = String.format("%s-host", VMWARE_TO_KVM_PREFIX); String VMWARE_DISK = String.format("%s-disk", VMWARE_TO_KVM_PREFIX); String VMWARE_MAC_ADDRESSES = String.format("%s-mac-addresses", VMWARE_TO_KVM_PREFIX); + + // TPM + String VIRTUAL_TPM_ENABLED = "virtual.tpm.enabled"; + String VIRTUAL_TPM_MODEL = "virtual.tpm.model"; + String VIRTUAL_TPM_VERSION = "virtual.tpm.version"; + + // CPU mode and model, ADMIN only + String GUEST_CPU_MODE = "guest.cpu.mode"; + String GUEST_CPU_MODEL = "guest.cpu.model"; + + // Lease related + String INSTANCE_LEASE_EXPIRY_DATE = "leaseexpirydate"; + String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction"; + String INSTANCE_LEASE_EXECUTION = "leaseactionexecution"; + + // External orchestrator related + String MAC_ADDRESS = "mac_address"; + String EXPUNGE_EXTERNAL_VM = "expunge.external.vm"; + String EXTERNAL_DETAIL_PREFIX = "External:"; + String CLOUDSTACK_VM_DETAILS = "cloudstack.vm.details"; + String CLOUDSTACK_VLAN = "cloudstack.vlan"; } diff --git a/api/src/main/java/com/cloud/vm/VmDiskInfo.java b/api/src/main/java/com/cloud/vm/VmDiskInfo.java new file mode 100644 index 00000000000..b8779a8d77c --- /dev/null +++ b/api/src/main/java/com/cloud/vm/VmDiskInfo.java @@ -0,0 +1,39 @@ +/* + * 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.vm; + +import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; + +public class VmDiskInfo extends DiskOfferingInfo { + private Long _deviceId; + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops) { + super(diskOffering, size, minIops, maxIops); + } + + public VmDiskInfo(DiskOffering diskOffering, Long size, Long minIops, Long maxIops, Long deviceId) { + super(diskOffering, size, minIops, maxIops); + _deviceId = deviceId; + } + + public Long getDeviceId() { + return _deviceId; + } +} diff --git a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java index 3897df2d5e6..24e93af1562 100644 --- a/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java +++ b/api/src/main/java/com/cloud/vm/snapshot/VMSnapshot.java @@ -31,7 +31,8 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity enum State { Allocated("The VM snapshot is allocated but has not been created yet."), Creating("The VM snapshot is being created."), Ready( "The VM snapshot is ready to be used."), Reverting("The VM snapshot is being used to revert"), Expunging("The volume is being expunging"), Removed( - "The volume is destroyed, and can't be recovered."), Error("The volume is in error state, and can't be recovered"); + "The volume is destroyed, and can't be recovered."), Error("The volume is in error state, and can't be recovered"), + Hidden("The VM snapshot is hidden from the user and cannot be recovered."); String _description; @@ -60,6 +61,8 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity s_fsm.addTransition(Expunging, Event.ExpungeRequested, Expunging); s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed); s_fsm.addTransition(Expunging, Event.OperationFailed, Error); + s_fsm.addTransition(Expunging, Event.Hide, Hidden); + s_fsm.addTransition(Hidden, Event.ExpungeRequested, Expunging); } } @@ -68,7 +71,7 @@ public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity } enum Event { - CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, + CreateRequested, OperationFailed, OperationSucceeded, RevertRequested, ExpungeRequested, Hide, } @Override diff --git a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java index 005d47c85bc..46e4f1bc510 100644 --- a/api/src/main/java/org/apache/cloudstack/acl/RoleType.java +++ b/api/src/main/java/org/apache/cloudstack/acl/RoleType.java @@ -23,8 +23,11 @@ import com.google.common.base.Enums; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import java.util.Collection; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; +import java.util.Set; // Enum for default roles in CloudStack public enum RoleType { @@ -100,6 +103,30 @@ public enum RoleType { return roleId; } + public static int toCombinedMask(Collection roles) { + int combinedMask = 0; + if (roles != null) { + for (RoleType role : roles) { + combinedMask |= role.getMask(); + } + } + return combinedMask; + } + + public static Set fromCombinedMask(int combinedMask) { + Set roles = EnumSet.noneOf(RoleType.class); + for (RoleType roleType : RoleType.values()) { + if ((combinedMask & roleType.getMask()) != 0) { + roles.add(roleType); + } + } + if (roles.isEmpty()) { + roles.add(Unknown); + } + return roles; + } + + /** * This method returns the role account type if the role isn't null, else it returns the default account type. * */ diff --git a/api/src/main/java/org/apache/cloudstack/alert/AlertService.java b/api/src/main/java/org/apache/cloudstack/alert/AlertService.java index 1250284b5c2..d8e471756a0 100644 --- a/api/src/main/java/org/apache/cloudstack/alert/AlertService.java +++ b/api/src/main/java/org/apache/cloudstack/alert/AlertService.java @@ -73,6 +73,9 @@ public interface AlertService { public static final AlertType ALERT_TYPE_VM_SNAPSHOT = new AlertType((short)32, "ALERT.VM.SNAPSHOT", true); public static final AlertType ALERT_TYPE_VR_PUBLIC_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PUBLIC.IFACE.MTU", true); public static final AlertType ALERT_TYPE_VR_PRIVATE_IFACE_MTU = new AlertType((short)32, "ALERT.VR.PRIVATE.IFACE.MTU", true); + public static final AlertType ALERT_TYPE_EXTENSION_PATH_NOT_READY = new AlertType((short)33, "ALERT.TYPE.EXTENSION.PATH.NOT.READY", true); + public static final AlertType ALERT_TYPE_BACKUP_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_BACKUP_STORAGE, "ALERT.STORAGE.BACKUP", true); + public static final AlertType ALERT_TYPE_OBJECT_STORAGE = new AlertType(Capacity.CAPACITY_TYPE_OBJECT_STORAGE, "ALERT.STORAGE.OBJECT", true); public short getType() { return type; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java index f2f52cec969..4d33ba859a5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiCommandResourceType.java @@ -61,6 +61,7 @@ public enum ApiCommandResourceType { AffinityGroup(org.apache.cloudstack.affinity.AffinityGroup.class), InternalLbVm(com.cloud.network.router.VirtualRouter.class), DedicatedGuestVlanRange(com.cloud.network.GuestVlan.class), + GuestOsCategory(com.cloud.storage.GuestOsCategory.class), GuestOs(com.cloud.storage.GuestOS.class), GuestOsMapping(com.cloud.storage.GuestOSHypervisor.class), Network(com.cloud.network.Network.class), @@ -84,9 +85,11 @@ public enum ApiCommandResourceType { ObjectStore(org.apache.cloudstack.storage.object.ObjectStore.class), Bucket(org.apache.cloudstack.storage.object.Bucket.class), QuotaTariff(org.apache.cloudstack.quota.QuotaTariff.class), - KubernetesCluster(null), + KubernetesCluster(com.cloud.kubernetes.cluster.KubernetesCluster.class), KubernetesSupportedVersion(null), - SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class); + SharedFS(org.apache.cloudstack.storage.sharedfs.SharedFS.class), + Extension(org.apache.cloudstack.extension.Extension.class), + ExtensionCustomAction(org.apache.cloudstack.extension.ExtensionCustomAction.class); private final Class clazz; diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 3e8b329cac7..489d737b5bb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -23,15 +23,18 @@ public class ApiConstants { public static final String ACCOUNT_ID = "accountid"; public static final String ACCOUNT_IDS = "accountids"; public static final String ACCUMULATE = "accumulate"; + public static final String ACQUIRED = "acquired"; public static final String ACTIVATION_RULE = "activationrule"; public static final String ACTIVITY = "activity"; public static final String ADAPTER_TYPE = "adaptertype"; public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; + public static final String ALLOCATED = "allocated"; public static final String ALLOCATED_DATE = "allocateddate"; public static final String ALLOCATED_ONLY = "allocatedonly"; public static final String ALLOCATED_TIME = "allocated"; + public static final String ALLOWED_ROLE_TYPES = "allowedroletypes"; public static final String ALLOW_USER_FORCE_STOP_VM = "allowuserforcestopvm"; public static final String ANNOTATION = "annotation"; public static final String API_KEY = "apikey"; @@ -59,6 +62,7 @@ public class ApiConstants { public static final String BACKUP_STORAGE_AVAILABLE = "backupstorageavailable"; public static final String BACKUP_STORAGE_LIMIT = "backupstoragelimit"; public static final String BACKUP_STORAGE_TOTAL = "backupstoragetotal"; + public static final String BACKUP_VM_OFFERING_REMOVED = "vmbackupofferingremoved"; public static final String BACKUP_TOTAL = "backuptotal"; public static final String BASE64_IMAGE = "base64image"; public static final String BGP_PEERS = "bgppeers"; @@ -68,6 +72,7 @@ public class ApiConstants { public static final String BOOTABLE = "bootable"; public static final String BIND_DN = "binddn"; public static final String BIND_PASSWORD = "bindpass"; + public static final String BUS_ADDRESS = "busaddress"; public static final String BYTES_READ_RATE = "bytesreadrate"; public static final String BYTES_READ_RATE_MAX = "bytesreadratemax"; public static final String BYTES_READ_RATE_MAX_LENGTH = "bytesreadratemaxlength"; @@ -90,9 +95,13 @@ public class ApiConstants { public static final String CONVERT_INSTANCE_HOST_ID = "convertinstancehostid"; public static final String CONVERT_INSTANCE_STORAGE_POOL_ID = "convertinstancepoolid"; public static final String ENABLED_REVOCATION_CHECK = "enabledrevocationcheck"; + public static final String CLIENT_ADDRESS = "clientaddress"; + public static final String COMBINED_CAPACITY_ORDERING = "COMBINED"; public static final String CONTROLLER = "controller"; public static final String CONTROLLER_UNIT = "controllerunit"; + public static final String CONSOLE_ENDPOINT_CREATOR_ADDRESS = "consoleendpointcreatoraddress"; public static final String COPY_IMAGE_TAGS = "copyimagetags"; + public static final String CPU_OVERCOMMIT_RATIO = "cpuOvercommitRatio"; public static final String CSR = "csr"; public static final String PRIVATE_KEY = "privatekey"; public static final String DATASTORE_HOST = "datastorehost"; @@ -119,7 +128,12 @@ public class ApiConstants { public static final String CN = "cn"; public static final String COMMAND = "command"; public static final String CMD_EVENT_TYPE = "cmdeventtype"; + public static final String CNI_CONFIG = "cniconfig"; + public static final String CNI_CONFIG_ID = "cniconfigurationid"; + public static final String CNI_CONFIG_DETAILS = "cniconfigdetails"; + public static final String CNI_CONFIG_NAME = "cniconfigname"; public static final String COMPONENT = "component"; + public static final String CPU = "CPU"; public static final String CPU_CORE_PER_SOCKET = "cpucorepersocket"; public static final String CPU_NUMBER = "cpunumber"; public static final String CPU_SPEED = "cpuspeed"; @@ -133,6 +147,7 @@ public class ApiConstants { public static final String CUSTOMIZED = "customized"; public static final String CUSTOMIZED_IOPS = "customizediops"; public static final String CUSTOM_ID = "customid"; + public static final String CUSTOM_ACTION_ID = "customactionid"; public static final String CUSTOM_JOB_ID = "customjobid"; public static final String CURRENT_START_IP = "currentstartip"; public static final String CURRENT_END_IP = "currentendip"; @@ -140,10 +155,12 @@ public class ApiConstants { public static final String ENCRYPT_FORMAT = "encryptformat"; public static final String ENCRYPT_ROOT = "encryptroot"; public static final String ENCRYPTION_SUPPORTED = "encryptionsupported"; + public static final String ETCD_IPS = "etcdips"; public static final String MIN_IOPS = "miniops"; public static final String MAX_IOPS = "maxiops"; public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve"; public static final String DATACENTER_NAME = "datacentername"; + public static final String DATADISKS_DETAILS = "datadisksdetails"; public static final String DATADISK_OFFERING_LIST = "datadiskofferinglist"; public static final String DEFAULT_VALUE = "defaultvalue"; public static final String DELETE_PROTECTION = "deleteprotection"; @@ -152,10 +169,13 @@ public class ApiConstants { public static final String DESTINATION_ZONE_ID = "destzoneid"; public static final String DETAILS = "details"; public static final String DEVICE_ID = "deviceid"; + public static final String DEVICE_IDS = "deviceids"; + public static final String DEVICE_NAME = "devicename"; public static final String DIRECT_DOWNLOAD = "directdownload"; public static final String DISK = "disk"; public static final String DISK_OFFERING_ID = "diskofferingid"; public static final String NEW_DISK_OFFERING_ID = "newdiskofferingid"; + public static final String ORCHESTRATOR_REQUIRES_PREPARE_VM = "orchestratorrequirespreparevm"; public static final String OVERRIDE_DISK_OFFERING_ID = "overridediskofferingid"; public static final String DISK_KBS_READ = "diskkbsread"; public static final String DISK_KBS_WRITE = "diskkbswrite"; @@ -197,6 +217,7 @@ public class ApiConstants { public static final String END_IPV6 = "endipv6"; public static final String END_PORT = "endport"; public static final String ENTRY_TIME = "entrytime"; + public static final String ERROR_MESSAGE = "errormessage"; public static final String EVENT_ID = "eventid"; public static final String EVENT_TYPE = "eventtype"; public static final String EXPIRES = "expires"; @@ -207,6 +228,12 @@ public class ApiConstants { public static final String EXTRA_DHCP_OPTION_VALUE = "extradhcpvalue"; public static final String EXTERNAL = "external"; public static final String EXTERNAL_UUID = "externaluuid"; + public static final String EXTERNAL_DETAILS = "externaldetails"; + public static final String PARAMETERS = "parameters"; + public static final String EXTENSION = "extension"; + public static final String EXTENSION_ID = "extensionid"; + public static final String EXTENSION_NAME = "extensionname"; + public static final String EXTENSIONS_PATH = "extensionspath"; public static final String FENCE = "fence"; public static final String FETCH_LATEST = "fetchlatest"; public static final String FILESYSTEM = "filesystem"; @@ -215,9 +242,11 @@ public class ApiConstants { public static final String FORCED_DESTROY_LOCAL_STORAGE = "forcedestroylocalstorage"; public static final String FORCE_DELETE_HOST = "forcedeletehost"; public static final String FORCE_MS_TO_IMPORT_VM_FILES = "forcemstoimportvmfiles"; + public static final String FORCE_UPDATE_OS_TYPE = "forceupdateostype"; public static final String FORMAT = "format"; public static final String FOR_VIRTUAL_NETWORK = "forvirtualnetwork"; public static final String FOR_SYSTEM_VMS = "forsystemvms"; + public static final String FOR_PROVIDER = "forprovider"; public static final String FULL_PATH = "fullpath"; public static final String GATEWAY = "gateway"; public static final String IP6_GATEWAY = "ip6gateway"; @@ -262,6 +291,7 @@ public class ApiConstants { public static final String PREVIOUS_OWNER_ID = "previousownerid"; public static final String PREVIOUS_OWNER_NAME = "previousownername"; public static final String NEXT_ACL_RULE_ID = "nextaclruleid"; + public static final String NEXT_HOP = "nexthop"; public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash"; public static final String IMAGE_PATH = "imagepath"; public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported"; @@ -269,7 +299,10 @@ public class ApiConstants { public static final String INTERNAL_DNS2 = "internaldns2"; public static final String INTERNET_PROTOCOL = "internetprotocol"; public static final String INTERVAL_TYPE = "intervaltype"; - public static final String LOCATION_TYPE = "locationtype"; + public static final String INSTANCE_LEASE_DURATION = "leaseduration"; + public static final String INSTANCE_LEASE_ENABLED = "instanceleaseenabled"; + public static final String INSTANCE_LEASE_EXPIRY_ACTION = "leaseexpiryaction"; + public static final String INSTANCE_LEASE_EXPIRY_DATE= "leaseexpirydate"; public static final String IOPS_READ_RATE = "iopsreadrate"; public static final String IOPS_READ_RATE_MAX = "iopsreadratemax"; public static final String IOPS_READ_RATE_MAX_LENGTH = "iopsreadratemaxlength"; @@ -279,6 +312,7 @@ public class ApiConstants { public static final String IP_ADDRESS = "ipaddress"; public static final String IP_ADDRESSES = "ipaddresses"; public static final String IP6_ADDRESS = "ip6address"; + public static final String IP6_ADDRESSES = "ip6addresses"; public static final String IP_ADDRESS_ID = "ipaddressid"; public static final String IS_2FA_ENABLED = "is2faenabled"; public static final String IS_2FA_VERIFIED = "is2faverified"; @@ -296,6 +330,7 @@ public class ApiConstants { public static final String IS_EXTRACTABLE = "isextractable"; public static final String IS_FEATURED = "isfeatured"; public static final String IS_IMPLICIT = "isimplicit"; + public static final String IS_ISO = "isiso"; public static final String IS_PORTABLE = "isportable"; public static final String IS_PUBLIC = "ispublic"; public static final String IS_PERSISTENT = "ispersistent"; @@ -317,27 +352,36 @@ public class ApiConstants { public static final String LAST_BOOT = "lastboottime"; public static final String LAST_SERVER_START = "lastserverstart"; public static final String LAST_SERVER_STOP = "lastserverstop"; + public static final String LEASED = "leased"; public static final String LEVEL = "level"; public static final String LENGTH = "length"; public static final String LIMIT = "limit"; public static final String LIMIT_CPU_USE = "limitcpuuse"; public static final String LIST_HOSTS = "listhosts"; + public static final String LOCATION_TYPE = "locationtype"; public static final String LOCK = "lock"; public static final String LUN = "lun"; public static final String LBID = "lbruleid"; public static final String LB_PROVIDER = "lbprovider"; public static final String MAC_ADDRESS = "macaddress"; + public static final String MAC_ADDRESSES = "macaddresses"; + public static final String MANUAL_UPGRADE = "manualupgrade"; public static final String MAX = "max"; public static final String MAX_SNAPS = "maxsnaps"; public static final String MAX_BACKUPS = "maxbackups"; public static final String MAX_CPU_NUMBER = "maxcpunumber"; public static final String MAX_MEMORY = "maxmemory"; + public static final String MEMORY_OVERCOMMIT_RATIO = "memoryOvercommitRatio"; + public static final String MESSAGE = "message"; public static final String MIN_CPU_NUMBER = "mincpunumber"; public static final String MIN_MEMORY = "minmemory"; public static final String MIGRATION_TYPE = "migrationtype"; + public static final String MIGRATION_JOB_ID = "migrationjobid"; + public static final String MIGRATION_JOB_STATUS = "migrationjobstatus"; public static final String MIGRATIONS = "migrations"; public static final String MEMORY = "memory"; public static final String MODE = "mode"; + public static final String MOUNT_CKS_ISO_ON_VR = "mountcksisoonvr"; public static final String MULTI_ARCH = "ismultiarch"; public static final String NSX_MODE = "nsxmode"; public static final String NETWORK_MODE = "networkmode"; @@ -348,12 +392,15 @@ public class ApiConstants { public static final String NETMASK = "netmask"; public static final String NEW_NAME = "newname"; public static final String NIC = "nic"; + public static final String NICS = "nics"; public static final String NIC_NETWORK_LIST = "nicnetworklist"; public static final String NIC_IP_ADDRESS_LIST = "nicipaddresslist"; public static final String NIC_MULTIQUEUE_NUMBER = "nicmultiqueuenumber"; public static final String NIC_PACKED_VIRTQUEUES_ENABLED = "nicpackedvirtqueuesenabled"; public static final String NEW_START_IP = "newstartip"; public static final String NEW_END_IP = "newendip"; + public static final String KUBERNETES_NODE_VERSION = "kubernetesnodeversion"; + public static final String NUMA_NODE = "numanode"; public static final String NUM_RETRIES = "numretries"; public static final String OFFER_HA = "offerha"; public static final String OS_DISTRIBUTION = "osdistribution"; @@ -370,6 +417,13 @@ public class ApiConstants { public static final String OS_TYPE_ID = "ostypeid"; public static final String OS_DISPLAY_NAME = "osdisplayname"; public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor"; + public static final String GPU_CARD_ID = "gpucardid"; + public static final String GPU_CARD_NAME = "gpucardname"; + public static final String GPU_COUNT = "gpucount"; + public static final String GPU_DISPLAY = "gpudisplay"; + public static final String GPU_DEVICE_TYPE = "gpudevicetype"; + public static final String GPU_ENABLED = "gpuenabled"; + public static final String MAX_VGPU_PER_PHYSICAL_GPU = "maxvgpuperphysicalgpu"; public static final String GUEST_OS_LIST = "guestoslist"; public static final String GUEST_OS_COUNT = "guestoscount"; public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled"; @@ -381,14 +435,17 @@ public class ApiConstants { public static final String PARENT = "parent"; public static final String PARENT_ID = "parentid"; public static final String PARENT_DOMAIN_ID = "parentdomainid"; + public static final String PARENT_GPU_DEVICE_ID = "parentgpudeviceid"; public static final String PARENT_SUBNET = "parentsubnet"; public static final String PARENT_TEMPLATE_ID = "parenttemplateid"; public static final String PASSWORD = "password"; + public static final String PCI_ROOT = "pciroot"; public static final String CURRENT_PASSWORD = "currentpassword"; public static final String SHOULD_UPDATE_PASSWORD = "update_passwd_on_host"; public static final String PASSWORD_ENABLED = "passwordenabled"; public static final String SSHKEY_ENABLED = "sshkeyenabled"; public static final String PATH = "path"; + public static final String PATH_READY = "pathready"; public static final String PAYLOAD = "payload"; public static final String PAYLOAD_URL = "payloadurl"; public static final String PEERS = "peers"; @@ -411,6 +468,8 @@ public class ApiConstants { public static final String POST_URL = "postURL"; public static final String POWER_STATE = "powerstate"; public static final String PRECEDENCE = "precedence"; + public static final String PREPARE_VM = "preparevm"; + public static final String PRESERVE_IP = "preserveip"; public static final String PRIVATE_INTERFACE = "privateinterface"; public static final String PRIVATE_IP = "privateip"; public static final String PRIVATE_PORT = "privateport"; @@ -427,11 +486,14 @@ public class ApiConstants { public static final String PUBLIC_END_PORT = "publicendport"; public static final String PUBLIC_ZONE = "publiczone"; public static final String PURGE_RESOURCES = "purgeresources"; + public static final String RAM = "RAM"; + public static final String REBALANCE = "rebalance"; public static final String RECEIVED_BYTES = "receivedbytes"; public static final String RECONNECT = "reconnect"; public static final String RECOVER = "recover"; public static final String REPAIR = "repair"; public static final String REQUIRES_HVM = "requireshvm"; + public static final String RESOURCES = "resources"; public static final String RESOURCE_COUNT = "resourcecount"; public static final String RESOURCE_NAME = "resourcename"; public static final String RESOURCE_TYPE = "resourcetype"; @@ -443,6 +505,7 @@ public class ApiConstants { public static final String REGISTERED = "registered"; public static final String QUALIFIERS = "qualifiers"; public static final String QUERY_FILTER = "queryfilter"; + public static final String QUIESCE_VM = "quiescevm"; public static final String SCHEDULE = "schedule"; public static final String SCHEDULE_ID = "scheduleid"; public static final String SCOPE = "scope"; @@ -456,7 +519,9 @@ public class ApiConstants { public static final String SENT = "sent"; public static final String SENT_BYTES = "sentbytes"; public static final String SERIAL = "serial"; + public static final String SERVICE_IP = "serviceip"; public static final String SERVICE_OFFERING_ID = "serviceofferingid"; + public static final String SERVICE_OFFERING_NAME = "serviceofferingname"; public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; @@ -466,6 +531,7 @@ public class ApiConstants { public static final String SIGNATURE = "signature"; public static final String SIGNATURE_VERSION = "signatureversion"; public static final String SINCE = "since"; + public static final String SITE_NAME = "sitename"; public static final String SIZE = "size"; public static final String SIZEGB = "sizegb"; public static final String SNAPSHOT = "snapshot"; @@ -473,6 +539,9 @@ public class ApiConstants { public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; public static final String SNAPSHOT_TYPE = "snapshottype"; public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; + + public static final String USE_STORAGE_REPLICATION = "usestoragereplication"; + public static final String SOURCE_CIDR_LIST = "sourcecidrlist"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; public static final String SSL_VERIFICATION = "sslverification"; @@ -485,19 +554,24 @@ public class ApiConstants { public static final String STATE = "state"; public static final String STATS = "stats"; public static final String STATUS = "status"; + public static final String STORAGE_TYPE = "storagetype"; + public static final String STORAGE_POLICY = "storagepolicy"; + public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; public static final String STORAGE_CAPABILITIES = "storagecapabilities"; public static final String STORAGE_CUSTOM_STATS = "storagecustomstats"; - public static final String STORAGE_MOTION_ENABLED = "storagemotionenabled"; - public static final String STORAGE_POLICY = "storagepolicy"; - public static final String STORAGE_POOL = "storagepool"; - public static final String STORAGE_TYPE = "storagetype"; public static final String SUBNET = "subnet"; public static final String OWNER = "owner"; public static final String SWAP_OWNER = "swapowner"; public static final String SYSTEM_VM_TYPE = "systemvmtype"; public static final String TAGS = "tags"; public static final String STORAGE_TAGS = "storagetags"; + public static final String STORAGE_ACCESS_GROUPS = "storageaccessgroups"; + public static final String STORAGE_ACCESS_GROUP = "storageaccessgroup"; + public static final String CLUSTER_STORAGE_ACCESS_GROUPS = "clusterstorageaccessgroups"; + public static final String POD_STORAGE_ACCESS_GROUPS = "podstorageaccessgroups"; + public static final String ZONE_STORAGE_ACCESS_GROUPS = "zonestorageaccessgroups"; public static final String SUCCESS = "success"; + public static final String SUCCESS_MESSAGE = "successmessage"; public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine"; public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot"; public static final String TARGET_IQN = "targetiqn"; @@ -510,6 +584,7 @@ public class ApiConstants { public static final String TIMEOUT = "timeout"; public static final String TIMEZONE = "timezone"; public static final String TIMEZONEOFFSET = "timezoneoffset"; + public static final String TENANT_NAME = "tenantname"; public static final String TOTAL = "total"; public static final String TOTAL_SUBNETS = "totalsubnets"; public static final String TYPE = "type"; @@ -517,6 +592,7 @@ public class ApiConstants { public static final String TRUST_STORE_PASSWORD = "truststorepass"; public static final String URL = "url"; public static final String USAGE_INTERFACE = "usageinterface"; + public static final String USED = "used"; public static final String USED_SUBNETS = "usedsubnets"; public static final String USED_IOPS = "usediops"; public static final String USER_DATA = "userdata"; @@ -536,8 +612,16 @@ public class ApiConstants { public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist"; public static final String USER_SECRET_KEY = "usersecretkey"; public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork"; + public static final String USE_VIRTUAL_ROUTER_IP_RESOLVER = "userouteripresolver"; public static final String UPDATE_IN_SEQUENCE = "updateinsequence"; + public static final String VALIDATION_FORMAT = "validationformat"; public static final String VALUE = "value"; + public static final String VALUE_OPTIONS = "valueoptions"; + public static final String VENDOR_ID = "vendorid"; + public static final String VENDOR_NAME = "vendorname"; + public static final String VGPU_PROFILE_ID = "vgpuprofileid"; + public static final String VGPU_PROFILE_NAME = "vgpuprofilename"; + public static final String VIRTUAL_MACHINE = "virtualmachine"; public static final String VIRTUAL_MACHINE_ID = "virtualmachineid"; public static final String VIRTUAL_MACHINE_IDS = "virtualmachineids"; public static final String VIRTUAL_MACHINE_NAME = "virtualmachinename"; @@ -552,6 +636,12 @@ public class ApiConstants { public static final String VLAN = "vlan"; public static final String VLAN_RANGE = "vlanrange"; + public static final String WORKER_SERVICE_OFFERING_ID = "workerofferingid"; + public static final String WORKER_SERVICE_OFFERING_NAME = "workerofferingname"; + public static final String CONTROL_SERVICE_OFFERING_ID = "controlofferingid"; + public static final String CONTROL_SERVICE_OFFERING_NAME = "controlofferingname"; + public static final String ETCD_SERVICE_OFFERING_ID = "etcdofferingid"; + public static final String ETCD_SERVICE_OFFERING_NAME = "etcdofferingname"; public static final String REMOVE_VLAN = "removevlan"; public static final String VLAN_ID = "vlanid"; public static final String ISOLATED_PVLAN = "isolatedpvlan"; @@ -561,8 +651,10 @@ public class ApiConstants { public static final String IS_DEDICATED = "isdedicated"; public static final String TAKEN = "taken"; public static final String VM_AVAILABLE = "vmavailable"; + public static final String VM_DETAILS = "vmdetails"; public static final String VM_LIMIT = "vmlimit"; public static final String VM_TOTAL = "vmtotal"; + public static final String VM_SETTINGS = "vmsettings"; public static final String VM_TYPE = "vmtype"; public static final String VNET = "vnet"; public static final String IS_VOLATILE = "isvolatile"; @@ -630,7 +722,7 @@ public class ApiConstants { public static final String NETWORK_DEVICE_PARAMETER_LIST = "networkdeviceparameterlist"; public static final String ZONE_TOKEN = "zonetoken"; public static final String DHCP_PROVIDER = "dhcpprovider"; - public static final String RESULT = "success"; + public static final String RESULT = "result"; public static final String RESUME = "resume"; public static final String LUN_ID = "lunId"; public static final String IQN = "iqn"; @@ -744,6 +836,7 @@ public class ApiConstants { public static final String LIST_ALL = "listall"; public static final String LIST_ONLY_REMOVED = "listonlyremoved"; public static final String LIST_SYSTEM_VMS = "listsystemvms"; + public static final String LIST_VM_DETAILS = "listvmdetails"; public static final String IP_RANGES = "ipranges"; public static final String IPV4_ROUTING = "ip4routing"; public static final String IPV4_ROUTES = "ip4routes"; @@ -879,6 +972,8 @@ public class ApiConstants { public static final String NETWORK = "network"; public static final String VPC_ID = "vpcid"; public static final String VPC_NAME = "vpcname"; + public static final String VPC_GATEWAY_ID = "vpcgatewayid"; + public static final String VPC_GATEWAY_IP = "vpcgatewayip"; public static final String GATEWAY_ID = "gatewayid"; public static final String CAN_USE_FOR_DEPLOY = "canusefordeploy"; public static final String RESOURCE_IDS = "resourceids"; @@ -902,6 +997,7 @@ public class ApiConstants { public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; public static final String FOR_NSX = "fornsx"; + public static final String FOR_CKS = "forcks"; public static final String NSX_SUPPORT_LB = "nsxsupportlb"; public static final String NSX_SUPPORTS_INTERNAL_LB = "nsxsupportsinternallb"; public static final String FOR_TUNGSTEN = "fortungsten"; @@ -1021,6 +1117,7 @@ public class ApiConstants { public static final String RESOURCE_DETAILS = "resourcedetails"; public static final String RESOURCE_ICON = "icon"; + public static final String RESOURCE_MAP = "resourcemap"; public static final String EXPUNGE = "expunge"; public static final String FOR_DISPLAY = "fordisplay"; public static final String PASSIVE = "passive"; @@ -1052,10 +1149,12 @@ public class ApiConstants { public static final String NETWORK_SPANNED_ZONES = "zonesnetworkspans"; public static final String METADATA = "metadata"; public static final String PHYSICAL_SIZE = "physicalsize"; + public static final String CHAIN_SIZE = "chainsize"; public static final String OVM3_POOL = "ovm3pool"; public static final String OVM3_CLUSTER = "ovm3cluster"; public static final String OVM3_VIP = "ovm3vip"; public static final String CLEAN_UP_DETAILS = "cleanupdetails"; + public static final String CLEAN_UP_PARAMETERS = "cleanupparameters"; public static final String VIRTUAL_SIZE = "virtualsize"; public static final String NETSCALER_CONTROLCENTER_ID = "netscalercontrolcenterid"; public static final String NETSCALER_SERVICEPACKAGE_ID = "netscalerservicepackageid"; @@ -1063,6 +1162,7 @@ public class ApiConstants { public static final String ZONE_ID_LIST = "zoneids"; public static final String DESTINATION_ZONE_ID_LIST = "destzoneids"; + public static final String STORAGE_ID_LIST = "storageids"; public static final String ADMIN = "admin"; public static final String CHECKSUM_PARAMETER_PREFIX_DESCRIPTION = "The parameter containing the checksum will be considered a MD5sum if it is not prefixed\n" + " and just a plain ascii/utf8 representation of a hexadecimal string. If it is required to\n" @@ -1110,6 +1210,10 @@ public class ApiConstants { public static final String MASTER_NODES = "masternodes"; public static final String NODE_IDS = "nodeids"; public static final String CONTROL_NODES = "controlnodes"; + public static final String ETCD_NODES = "etcdnodes"; + public static final String EXTERNAL_NODES = "externalnodes"; + public static final String IS_EXTERNAL_NODE = "isexternalnode"; + public static final String IS_ETCD_NODE = "isetcdnode"; public static final String MIN_SEMANTIC_VERSION = "minimumsemanticversion"; public static final String MIN_KUBERNETES_VERSION_ID = "minimumkubernetesversionid"; public static final String NODE_ROOT_DISK_SIZE = "noderootdisksize"; @@ -1118,6 +1222,8 @@ public class ApiConstants { public static final String AUTOSCALING_ENABLED = "autoscalingenabled"; public static final String MIN_SIZE = "minsize"; public static final String MAX_SIZE = "maxsize"; + public static final String NODE_TYPE_OFFERING_MAP = "nodeofferings"; + public static final String NODE_TYPE_TEMPLATE_MAP = "nodetemplates"; public static final String BOOT_TYPE = "boottype"; public static final String BOOT_MODE = "bootmode"; @@ -1150,6 +1256,7 @@ public class ApiConstants { public static final String PENDING_JOBS_COUNT = "pendingjobscount"; public static final String AGENTS_COUNT = "agentscount"; public static final String AGENTS = "agents"; + public static final String LAST_AGENTS = "lastagents"; public static final String PUBLIC_MTU = "publicmtu"; public static final String PRIVATE_MTU = "privatemtu"; @@ -1166,6 +1273,10 @@ public class ApiConstants { public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid"; public static final String HAS_RULES = "hasrules"; public static final String NSX_DETAIL_KEY = "forNsx"; + public static final String NETRIS_DETAIL_KEY = "forNetris"; + public static final String NETRIS_TAG = "netristag"; + public static final String NETRIS_VXLAN_ID = "netrisvxlanid"; + public static final String NETRIS_URL = "netrisurl"; public static final String DISK_PATH = "diskpath"; public static final String IMPORT_SOURCE = "importsource"; public static final String TEMP_PATH = "temppath"; @@ -1219,8 +1330,28 @@ public class ApiConstants { "however, the following formats are also accepted: \"yyyy-MM-dd HH:mm:ss\" (e.g.: \"2023-01-01 12:00:00\") and \"yyyy-MM-dd\" (e.g.: \"2023-01-01\" - if the time is not " + "added, it will be interpreted as \"23:59:59\"). If the recommended format is not used, the date will be considered in the server timezone."; + public static final String PARAMETER_DESCRIPTION_MAX_BACKUPS = "The maximum number of backups to keep for a VM. " + + "If \"0\", no retention policy will be applied and, thus, no backups from the schedule will be automatically deleted. " + + "This parameter is only supported for the Dummy, NAS and EMC Networker backup provider."; + public static final String VMWARE_DC = "vmwaredc"; + public static final String CSS = "css"; + + public static final String JSON_CONFIGURATION = "jsonconfiguration"; + + public static final String COMMON_NAMES = "commonnames"; + + public static final String COMMON_NAME = "commonname"; + + public static final String DOMAIN_IDS = "domainids"; + + public static final String SHOW_PUBLIC = "showpublic"; + + public static final String LIST_ONLY_DEFAULT_THEME = "listonlydefaulttheme"; + + public static final String RECURSIVE_DOMAINS = "recursivedomains"; + /** * This enum specifies IO Drivers, each option controls specific policies on I/O. * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0). @@ -1273,6 +1404,10 @@ public class ApiConstants { all, resource, min; } + public enum ExtensionDetails { + all, resource, external, min; + } + public enum ApiKeyAccess { DISABLED(false), ENABLED(true), diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java b/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java index d4fdeddc9a9..616c37484d8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiErrorCode.java @@ -22,6 +22,7 @@ package org.apache.cloudstack.api; */ public enum ApiErrorCode { + BAD_REQUEST(400), UNAUTHORIZED(401), UNAUTHORIZED2FA(511), METHOD_NOT_ALLOWED(405), @@ -30,6 +31,7 @@ public enum ApiErrorCode { UNSUPPORTED_ACTION_ERROR(432), API_LIMIT_EXCEED(429), + SERVICE_UNAVAILABLE(503), INTERNAL_ERROR(530), ACCOUNT_ERROR(531), ACCOUNT_RESOURCE_LIMIT_ERROR(532), diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java index cbbcdc3bda4..cb75939d6bc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiServerService.java @@ -48,4 +48,6 @@ public interface ApiServerService { boolean forgotPassword(UserAccount userAccount, Domain domain); boolean resetPassword(UserAccount userAccount, String token, String password); + + boolean isPostRequestsAndTimestampsEnforced(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java index 457afdc8847..8f47d51b19d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -39,6 +39,7 @@ import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gpu.GpuService; import org.apache.cloudstack.network.RoutedIpv4Manager; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; @@ -94,6 +95,7 @@ import com.cloud.utils.ReflectUtil; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.UUIDManager; import com.cloud.vm.UserVmService; +import com.cloud.vm.VmDetailConstants; import com.cloud.vm.snapshot.VMSnapshotService; public abstract class BaseCmd { @@ -130,6 +132,8 @@ public abstract class BaseCmd { @Inject public UserVmService _userVmService; @Inject + public GpuService gpuService; + @Inject public ManagementService _mgr; @Inject public StorageService _storageService; @@ -484,4 +488,14 @@ public abstract class BaseCmd { } return detailsMap; } + + public Map convertExternalDetailsToMap(Map externalDetails) { + Map customparameterMap = convertDetailsToMap(externalDetails); + Map details = new HashMap<>(); + for (String key : customparameterMap.keySet()) { + String value = customparameterMap.get(key); + details.put(VmDetailConstants.EXTERNAL_DETAIL_PREFIX + key, value); + } + return details; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java index 9a8282df112..8489bf05ec7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseUpdateTemplateOrIsoCmd.java @@ -51,6 +51,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { description = "the ID of the OS type that best represents the OS of this image.") private Long osTypeId; + @Parameter(name = ApiConstants.FORCE_UPDATE_OS_TYPE, type = CommandType.BOOLEAN, since = "4.21", description = "Force OS type update. Warning: Updating OS type will " + + "update the guest OS configuration for all the existing Instances deployed with this template/iso, which may affect their behavior.") + private Boolean forceUpdateOsType; + @Parameter(name = ApiConstants.FORMAT, type = CommandType.STRING, description = "the format for the image") private String format; @@ -112,6 +116,10 @@ public abstract class BaseUpdateTemplateOrIsoCmd extends BaseCmd { return osTypeId; } + public Boolean getForceUpdateOsType() { + return forceUpdateOsType; + } + public Boolean getPasswordEnabled() { return passwordEnabled; } diff --git a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java index ea0d946ee41..8e92e877f5c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/main/java/org/apache/cloudstack/api/ResponseGenerator.java @@ -22,27 +22,24 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.cloud.bgp.ASNumber; -import com.cloud.bgp.ASNumberRange; - -import org.apache.cloudstack.storage.object.Bucket; +import org.apache.cloudstack.api.response.ConsoleSessionResponse; +import org.apache.cloudstack.consoleproxy.ConsoleSession; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants.HostDetails; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; -import org.apache.cloudstack.api.response.AccountResponse; -import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; import org.apache.cloudstack.api.response.ASNRangeResponse; import org.apache.cloudstack.api.response.ASNumberResponse; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ApplicationLoadBalancerResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.AutoScaleVmProfileResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupRepositoryResponse; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.BucketResponse; import org.apache.cloudstack.api.response.CapacityResponse; @@ -60,24 +57,25 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.DomainRouterResponse; import org.apache.cloudstack.api.response.EventResponse; import org.apache.cloudstack.api.response.ExtractResponse; -import org.apache.cloudstack.api.response.SharedFSResponse; import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.GuestVlanResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; import org.apache.cloudstack.api.response.HypervisorGuestOsNamesResponse; import org.apache.cloudstack.api.response.IPAddressResponse; -import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.InternalLoadBalancerElementResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse; +import org.apache.cloudstack.api.response.IpQuarantineResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; @@ -115,6 +113,7 @@ import org.apache.cloudstack.api.response.SecondaryStorageHeuristicsResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.ServiceResponse; +import org.apache.cloudstack.api.response.SharedFSResponse; import org.apache.cloudstack.api.response.Site2SiteCustomerGatewayResponse; import org.apache.cloudstack.api.response.Site2SiteVpnConnectionResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; @@ -144,7 +143,6 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupOffering; import org.apache.cloudstack.backup.BackupRepository; import org.apache.cloudstack.backup.BackupSchedule; @@ -153,16 +151,21 @@ import org.apache.cloudstack.config.ConfigurationGroup; import org.apache.cloudstack.direct.download.DirectDownloadCertificate; import org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMap; import org.apache.cloudstack.direct.download.DirectDownloadManager; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.region.Region; import org.apache.cloudstack.secstorage.heuristics.Heuristic; -import org.apache.cloudstack.storage.sharedfs.SharedFS; +import org.apache.cloudstack.storage.object.Bucket; import org.apache.cloudstack.storage.object.ObjectStore; +import org.apache.cloudstack.storage.sharedfs.SharedFS; import org.apache.cloudstack.usage.Usage; +import org.apache.cloudstack.vm.UnmanagedInstanceTO; +import com.cloud.bgp.ASNumber; +import com.cloud.bgp.ASNumberRange; import com.cloud.capacity.Capacity; import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceLimit; @@ -223,10 +226,11 @@ import com.cloud.projects.ProjectAccount; import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.resource.RollingMaintenanceManager; -import com.cloud.server.ResourceTag; import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceTag; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSHypervisor; +import com.cloud.storage.GuestOsCategory; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; @@ -240,14 +244,13 @@ import com.cloud.user.User; import com.cloud.user.UserAccount; import com.cloud.user.UserData; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Ip; import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; import com.cloud.vm.InstanceGroup; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; public interface ResponseGenerator { UserResponse createUserResponse(UserAccount user); @@ -310,6 +313,8 @@ public interface ResponseGenerator { PodResponse createPodResponse(Pod pod, Boolean showCapacities); + PodResponse createMinimalPodResponse(Pod pod); + ZoneResponse createZoneResponse(ResponseView view, DataCenter dataCenter, Boolean showCapacities, Boolean showResourceIcon); DataCenterGuestIpv6PrefixResponse createDataCenterGuestIpv6PrefixResponse(DataCenterGuestIpv6Prefix prefix); @@ -324,6 +329,8 @@ public interface ResponseGenerator { ClusterResponse createClusterResponse(Cluster cluster, Boolean showCapacities); + ClusterResponse createMinimalClusterResponse(Cluster cluster); + FirewallRuleResponse createPortForwardingRuleResponse(PortForwardingRule fwRule); IpForwardingRuleResponse createIpForwardingRuleResponse(StaticNatRule fwRule); @@ -481,6 +488,10 @@ public interface ResponseGenerator { AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup); + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory); + + GuestOSCategoryResponse createGuestOSCategoryResponse(GuestOsCategory guestOsCategory, boolean showIcon); + GuestOSResponse createGuestOSResponse(GuestOS os); GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor); @@ -523,8 +534,6 @@ public interface ResponseGenerator { UserDataResponse createUserDataResponse(UserData userData); - BackupResponse createBackupResponse(Backup backup); - BackupScheduleResponse createBackupScheduleResponse(BackupSchedule backup); BackupOfferingResponse createBackupOfferingResponse(BackupOffering policy); @@ -568,4 +577,10 @@ public interface ResponseGenerator { BackupRepositoryResponse createBackupRepositoryResponse(BackupRepository repository); SharedFSResponse createSharedFSResponse(ResponseView view, SharedFS sharedFS); + + void updateTemplateIsoResponsesForIcons(List responses, ResourceTag.ResourceObjectType type); + + GuiThemeResponse createGuiThemeResponse(GuiThemeJoin guiThemeJoin); + + ConsoleSessionResponse createConsoleSessionResponse(ConsoleSession consoleSession, ResponseView responseView); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java index e876dbc9b58..dedbb410ea5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/acl/project/ListProjectRolesCmd.java @@ -72,7 +72,7 @@ public class ListProjectRolesCmd extends BaseListCmd { @Override public void execute() { - List projectRoles; + List projectRoles = new ArrayList<>(); if (getProjectId() != null && getProjectRoleId() != null) { projectRoles = Collections.singletonList(projRoleService.findProjectRole(getProjectRoleId(), getProjectId())); } else if (StringUtils.isNotBlank(getRoleName())) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java index 69cb43ce40e..3aef11b92e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/AddClusterCmd.java @@ -19,21 +19,22 @@ package org.apache.cloudstack.api.command.admin.cluster; import java.util.ArrayList; import java.util.List; - -import com.cloud.cpu.CPU; -import org.apache.cloudstack.api.ApiCommandResourceType; +import java.util.Map; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import com.cloud.cpu.CPU; import com.cloud.exception.DiscoveryException; import com.cloud.exception.ResourceInUseException; import com.cloud.org.Cluster; @@ -43,7 +44,6 @@ import com.cloud.user.Account; requestHasSensitiveInfo = true, responseHasSensitiveInfo = false) public class AddClusterCmd extends BaseCmd { - @Parameter(name = ApiConstants.CLUSTER_NAME, type = CommandType.STRING, required = true, description = "the cluster name") private String clusterName; @@ -65,7 +65,7 @@ public class AddClusterCmd extends BaseCmd { @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, - description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3") + description = "hypervisor type of the cluster: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator,Ovm3,External") private String hypervisor; @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, @@ -118,6 +118,26 @@ public class AddClusterCmd extends BaseCmd { private String ovm3cluster; @Parameter(name = ApiConstants.OVM3_VIP, type = CommandType.STRING, required = false, description = "Ovm3 vip to use for pool (and cluster)") private String ovm3vip; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the cluster", + since = "4.21.0") + private List storageAccessGroups; + + + @Parameter(name = ApiConstants.EXTENSION_ID, + type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "UUID of the extension", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs to be added to the extension-resource mapping. Use the format externaldetails[i].=. Example: externaldetails[0].endpoint.url=https://example.com", + since = "4.21.0") + protected Map externalDetails; + public String getOvm3Pool() { return ovm3pool; } @@ -184,6 +204,10 @@ public class AddClusterCmd extends BaseCmd { return hypervisor; } + public Long getExtensionId() { + return extensionId; + } + public String getClusterType() { return clusterType; } @@ -192,6 +216,10 @@ public class AddClusterCmd extends BaseCmd { this.clusterType = type; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + @Override public long getEntityOwnerId() { return Account.ACCOUNT_ID_SYSTEM; @@ -214,6 +242,10 @@ public class AddClusterCmd extends BaseCmd { return CPU.CPUArch.fromType(arch); } + public Map getExternalDetails() { + return convertDetailsToMap(externalDetails); + } + @Override public void execute() { try { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java index 67d0678410c..b38278e3946 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmd.java @@ -17,8 +17,11 @@ package org.apache.cloudstack.api.command.admin.cluster; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import javax.inject.Inject; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +31,13 @@ import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.hypervisor.Hypervisor; import com.cloud.org.Cluster; import com.cloud.utils.Pair; @@ -36,6 +45,8 @@ import com.cloud.utils.Pair; requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListClustersCmd extends BaseListCmd { + @Inject + ExtensionHelper extensionHelper; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -68,6 +79,16 @@ public class ListClustersCmd extends BaseListCmd { @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the clusters") private Boolean showCapacities; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the clusters", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -112,25 +133,64 @@ public class ListClustersCmd extends BaseListCmd { return showCapacities; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListClustersCmd() { + + } + + public ListClustersCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + protected void updateClustersExtensions(final List clusterResponses) { + if (CollectionUtils.isEmpty(clusterResponses)) { + return; + } + Map idExtensionMap = new HashMap<>(); + for (ClusterResponse response : clusterResponses) { + if (!Hypervisor.HypervisorType.External.getHypervisorDisplayName().equals(response.getHypervisorType())) { + continue; + } + Long extensionId = extensionHelper.getExtensionIdForCluster(response.getInternalId()); + if (extensionId == null) { + continue; + } + Extension extension = idExtensionMap.computeIfAbsent(extensionId, id -> extensionHelper.getExtension(id)); + if (extension == null) { + continue; + } + response.setExtensionId(extension.getUuid()); + response.setExtensionName(extension.getName()); + } + } + protected Pair, Integer> getClusterResponses() { Pair, Integer> result = _mgr.searchForClusters(this); - List clusterResponses = new ArrayList(); + List clusterResponses = new ArrayList<>(); for (Cluster cluster : result.first()) { ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, showCapacities); clusterResponse.setObjectName("cluster"); clusterResponses.add(clusterResponse); } - return new Pair, Integer>(clusterResponses, result.second()); + updateClustersExtensions(clusterResponses); + return new Pair<>(clusterResponses, result.second()); } @Override public void execute() { Pair, Integer> clusterResponses = getClusterResponses(); - ListResponse response = new ListResponse(); + ListResponse response = new ListResponse<>(); response.setResponses(clusterResponses.first(), clusterResponses.second()); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java index c4ee87380ed..c160cfd2e03 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/cluster/UpdateClusterCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.admin.cluster; +import java.util.Map; + import com.cloud.cpu.CPU; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -60,6 +62,12 @@ public class UpdateClusterCmd extends BaseCmd { since = "4.20") private String arch; + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs to be added to the extension-resource mapping. Use the format externaldetails[i].=. Example: externaldetails[0].endpoint.url=https://example.com", + since = "4.21.0") + protected Map externalDetails; + public String getClusterName() { return clusterName; } @@ -122,6 +130,10 @@ public class UpdateClusterCmd extends BaseCmd { return CPU.CPUArch.fromType(arch); } + public Map getExternalDetails() { + return convertDetailsToMap(externalDetails); + } + @Override public void execute() { Cluster cluster = _resourceService.getCluster(getId()); @@ -130,7 +142,7 @@ public class UpdateClusterCmd extends BaseCmd { } Cluster result = _resourceService.updateCluster(this); if (result != null) { - ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(cluster, false); + ClusterResponse clusterResponse = _responseGenerator.createClusterResponse(result, false); clusterResponse.setResponseName(getCommandName()); this.setResponseObject(clusterResponse); } else { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java new file mode 100644 index 00000000000..2faad89bf67 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmd.java @@ -0,0 +1,122 @@ +// 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.gpu; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "createGpuCard", description = "Creates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class CreateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, required = true, + description = "the device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, required = true, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, required = true, + description = "the vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, required = true, + description = "the vendor ID of the GPU card") + private String vendorId; + + // Optional parameters for the passthrough vGPU profile display properties + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB for the passthrough vGPU profile") + private Long videoRam; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public Long getVideoRam() { + return videoRam; + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, + ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + GpuCard gpuCard = gpuService.createGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java new file mode 100644 index 00000000000..e6386082a44 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmd.java @@ -0,0 +1,123 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "createGpuDevice", description = "Creates a GPU device manually on a host", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class CreateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host where the GPU device is located") + private Long hostId; + + @Parameter(name = ApiConstants.BUS_ADDRESS, type = CommandType.STRING, required = true, + description = "PCI bus address of the GPU device (e.g., 0000:01:00.0) or UUID for MDEV devices.") + private String busAddress; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "ID of the GPU card type") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + required = true, description = "ID of the vGPU profile") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.TYPE, type = CommandType.STRING, + description = "Type of GPU device (PCI, MDEV, VGPUOnly). Defaults to PCI.") + private String type; + + @Parameter(name = ApiConstants.PARENT_GPU_DEVICE_ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the parent GPU device (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "NUMA node of the GPU device (e.g., 0, 1, etc.). This is optional and can be used to " + + "specify the NUMA node for the GPU device which is used during allocation. Defaults to -1") + private String numaNode; + + public Long getHostId() { + return hostId; + } + + public String getBusAddress() { + return busAddress; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = GpuDevice.DeviceType.PCI; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + if (StringUtils.isBlank(numaNode)) { + return "-1"; // Default value for NUMA node + } + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.createGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java new file mode 100644 index 00000000000..3210773b6f4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmd.java @@ -0,0 +1,131 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "createVgpuProfile", description = "Creates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class CreateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, + description = "the name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + required = true, description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "Max vGPU per physical GPU. This is used to calculate capacity.") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Long getCardId() { + return cardId; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.createVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to create vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java new file mode 100644 index 00000000000..9a510ecdead --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmd.java @@ -0,0 +1,75 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + + +@APICommand(name = "deleteGpuCard", description = "Deletes a GPU card definition from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuCard(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java new file mode 100644 index 00000000000..9224afc66ec --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "deleteGpuDevice", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteGpuDevices(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java new file mode 100644 index 00000000000..a09a04199f5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmd.java @@ -0,0 +1,76 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +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.VgpuProfileResponse; + + +@APICommand(name = "deleteVgpuProfile", description = "Deletes a vGPU profile from the system", + responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DeleteVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public void execute() { + try { + boolean success = gpuService.deleteVgpuProfile(this); + if (success) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to delete vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java new file mode 100644 index 00000000000..2ac07a9fb3a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmd.java @@ -0,0 +1,63 @@ +// 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.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; + + +@APICommand(name = "discoverGpuDevices", description = "Discovers available GPU devices on a host", + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class DiscoverGpuDevicesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = HostResponse.class, required = true, + description = "ID of the host") + private Long id; + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Discovering GPU Devices on host id: " + getId()); + ListResponse response = gpuService.discoverGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java new file mode 100644 index 00000000000..b3c57713fcd --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdmin.java @@ -0,0 +1,74 @@ +// 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.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Full, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuDevicesCmdByAdmin extends ListGpuDevicesCmd implements AdminCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "ID of the GPU device") + private Long id; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, + description = "the host ID where the GPU device is attached") + private Long hostId; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with the GPU device") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "the vGPU profile ID assigned to the GPU device") + private Long vgpuProfileId; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getHostId() { + return hostId; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java new file mode 100644 index 00000000000..5dfe6c3deee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmd.java @@ -0,0 +1,78 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "manageGpuDevice", description = "Manages a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class ManageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.enableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to enable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java new file mode 100644 index 00000000000..46de23ec44b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmd.java @@ -0,0 +1,79 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import java.util.List; + +@APICommand(name = "unmanageGpuDevice", description = "Unmanage a GPU device", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin}) +public class UnmanageGpuDeviceCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, + entityType = GpuDeviceResponse.class, required = true, + description = "comma separated list of IDs of the GPU device") + private List ids; + + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public List getIds() { + return ids; + } + + @Override + public void execute() { + try { + if (gpuService.disableGpuDevice(this)) { + SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to disable GPU device"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to disable GPU device: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java new file mode 100644 index 00000000000..0061149a985 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmd.java @@ -0,0 +1,99 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.gpu.GpuCard; + + +@APICommand(name = "updateGpuCard", description = "Updates a GPU card definition in the system", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateGpuCardCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, required = true, + description = "the ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "the device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the display name of the GPU card") + private String name; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "the vendor name of the GPU card") + private String vendorName; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getDeviceName() { + return deviceName; + } + + public String getName() { + return name; + } + + public String getVendorName() { + return vendorName; + } + + @Override + public void execute() { + try { + GpuCard gpuCard = gpuService.updateGpuCard(this); + if (gpuCard != null) { + GpuCardResponse response = new GpuCardResponse(gpuCard); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update GPU card: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java new file mode 100644 index 00000000000..5ad6e6e1a6f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmd.java @@ -0,0 +1,109 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + + +@APICommand(name = "updateGpuDevice", description = "Updates an existing GPU device", + responseObject = GpuDeviceResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGpuDeviceCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuDeviceResponse.class, required = true, + description = "ID of the GPU device to update") + private Long id; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "New GPU card ID") + private Long gpuCardId; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "New vGPU profile ID") + private Long vgpuProfileId; + + @Parameter(name = "type", type = CommandType.STRING, description = "New type of GPU device (PCI, MDEV, VGPUOnly)") + private String type; + + @Parameter(name = "parentgpudeviceid", type = CommandType.UUID, entityType = GpuDeviceResponse.class, + description = "New parent GPU device ID (for virtual GPU devices)") + private Long parentGpuDeviceId; + + @Parameter(name = ApiConstants.NUMA_NODE, type = CommandType.STRING, + description = "New NUMA node of the GPU device") + private String numaNode; + + public Long getId() { + return id; + } + + public Long getGpuCardId() { + return gpuCardId; + } + + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public GpuDevice.DeviceType getType() { + GpuDevice.DeviceType deviceType = null; + if (StringUtils.isNotBlank(type)) { + deviceType = EnumUtils.getEnumIgnoreCase(GpuDevice.DeviceType.class, type); + if (deviceType == null) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Invalid GPU device type: " + type); + } + } + return deviceType; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + @Override + public void execute() { + try { + GpuDeviceResponse response = gpuService.updateGpuDevice(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java new file mode 100644 index 00000000000..c8d60739bd4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmd.java @@ -0,0 +1,129 @@ +// 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.gpu; + +import com.cloud.user.Account; +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + + +@APICommand(name = "updateVgpuProfile", description = "Updates a vGPU profile in the system", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.21.0", authorized = {RoleType.Admin}) +public class UpdateVgpuProfileCmd extends BaseCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, required = true, + description = "the ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the vGPU profile") + private String profileName; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, + description = "the description of the vGPU profile") + private String description; + + @Parameter(name = ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU, type = CommandType.LONG, + description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @Parameter(name = ApiConstants.VIDEORAM, type = CommandType.LONG, + description = "the video RAM size in MB") + private Long videoRam; + + @Parameter(name = ApiConstants.MAXHEADS, type = CommandType.LONG, + description = "the maximum number of display heads") + private Long maxHeads; + + @Parameter(name = ApiConstants.MAXRESOLUTIONX, type = CommandType.LONG, + description = "the maximum X resolution") + private Long maxResolutionX; + + @Parameter(name = ApiConstants.MAXRESOLUTIONY, type = CommandType.LONG, + description = "the maximum Y resolution") + private Long maxResolutionY; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getProfileName() { + return profileName; + } + + public String getDescription() { + return description; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + @Override + public void execute() { + try { + VgpuProfileResponse response = gpuService.updateVgpuProfile(this); + if (response != null) { + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vGPU profile"); + } + } catch (Exception e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + "Failed to update vGPU profile: " + e.getMessage()); + } + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java new file mode 100644 index 00000000000..f099de43f5d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmd.java @@ -0,0 +1,93 @@ +// 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.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "addOsCategory", + description = "Adds a new OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class AddGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name of the OS category", + required = true) + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public boolean isFeatured() { + return Boolean.TRUE.equals(featured); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.addGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new OS category"); + } + + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java new file mode 100644 index 00000000000..8cf62ee2aab --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/DeleteGuestOsCategoryCmd.java @@ -0,0 +1,89 @@ +// 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.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; +import org.apache.cloudstack.api.response.SuccessResponse; + +import com.cloud.user.Account; + + +@APICommand(name = "deleteOsCategory", + description = "Deletes an OS category", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class DeleteGuestOsCategoryCmd extends BaseCmd { + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteGuestOsCategory(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove OS category"); + } + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java new file mode 100644 index 00000000000..4041967abe8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmd.java @@ -0,0 +1,113 @@ +// 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.guest; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; + +import com.cloud.storage.GuestOsCategory; +import com.cloud.user.Account; + +@APICommand(name = "updateOsCategory", + description = "Updates an OS category", + responseObject = GuestOSCategoryResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + since = "4.21.0", + authorized = {RoleType.Admin}) +public class UpdateGuestOsCategoryCmd extends BaseCmd { + + + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + required = true, description = "ID of the OS category") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "Name for the OS category") + private String name; + + @Parameter(name = ApiConstants.IS_FEATURED, type = CommandType.BOOLEAN, + description = "Whether the category is featured or not") + private Boolean featured; + + @Parameter(name = ApiConstants.SORT_KEY, type = CommandType.INTEGER, + description = "sort key of the OS category for listing") + private Integer sortKey; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Boolean isFeatured() { + return featured; + } + + public Integer getSortKey() { + return sortKey; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + GuestOsCategory guestOs = _mgr.updateGuestOsCategory(this); + if (guestOs != null) { + GuestOSCategoryResponse response = _responseGenerator.createGuestOSCategoryResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update OS category"); + } + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.GuestOsCategory; + } + + @Override + public Long getApiResourceId() { + return getId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java index c98cd149ef3..59909e09854 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java @@ -16,8 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.admin.guest; -import org.apache.commons.collections.MapUtils; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -25,18 +29,14 @@ 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.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.GuestOSResponse; -import org.apache.cloudstack.acl.RoleType; +import org.apache.commons.collections.MapUtils; import com.cloud.event.EventTypes; import com.cloud.storage.GuestOS; import com.cloud.user.Account; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - @APICommand(name = "updateGuestOs", description = "Updates the information about Guest OS", responseObject = GuestOSResponse.class, since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class UpdateGuestOsCmd extends BaseAsyncCmd { @@ -50,7 +50,7 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "UUID of the Guest OS") private Long id; - @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS") + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, description = "Unique display name for Guest OS") private String osDisplayName; @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, required = false, description = "Map of (key/value pairs)") @@ -59,6 +59,12 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { @Parameter(name="forDisplay", type=CommandType.BOOLEAN, description="whether this guest OS is available for end users", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + description = "the ID of the OS category", since = "4.21.0") + private Long osCategoryId; + + + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -71,9 +77,9 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { return osDisplayName; } - public Map getDetails() { - Map detailsMap = new HashMap<>();; - if (MapUtils.isNotEmpty(detailsMap)) { + public Map getDetails() { + Map detailsMap = new HashMap<>(); + if (MapUtils.isNotEmpty(details)) { Collection servicesCollection = details.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -90,6 +96,10 @@ public class UpdateGuestOsCmd extends BaseAsyncCmd { return display; } + public Long getOsCategoryId() { + return osCategoryId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java index ca27837aa88..cc124ab8106 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddHostCmd.java @@ -18,7 +18,7 @@ package org.apache.cloudstack.api.command.admin.host; import java.util.ArrayList; import java.util.List; - +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -75,6 +75,18 @@ public class AddHostCmd extends BaseCmd { @Parameter(name = ApiConstants.HOST_TAGS, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list of tags to be added to the host") private List hostTags; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the host", + since = "4.21.0") + private List storageAccessGroups; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -115,10 +127,18 @@ public class AddHostCmd extends BaseCmd { return hostTags; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + public String getAllocationState() { return allocationState; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java index 5e229521efe..a71150a69e2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java @@ -21,7 +21,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -35,7 +34,9 @@ import org.apache.cloudstack.api.response.ManagementServerResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -109,6 +110,14 @@ public class ListHostsCmd extends BaseListCmd { @Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, description = "the id of the management server", since="4.21.0") private Long managementServerId; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, description = "CPU Arch of the host", since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -197,6 +206,22 @@ public class ListHostsCmd extends BaseListCmd { return managementServerId; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListHostsCmd() { + + } + + public ListHostsCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java index 397f9c80735..9baea58b7b5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/host/UpdateHostCmd.java @@ -16,8 +16,9 @@ // under the License. package org.apache.cloudstack.api.command.admin.host; -import com.cloud.host.Host; -import com.cloud.user.Account; +import java.util.List; +import java.util.Map; + import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,7 +29,8 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.HostResponse; -import java.util.List; +import com.cloud.host.Host; +import com.cloud.user.Account; @APICommand(name = "updateHost", description = "Updates a host.", responseObject = HostResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -67,6 +69,9 @@ public class UpdateHostCmd extends BaseCmd { @Parameter(name = ApiConstants.ANNOTATION, type = CommandType.STRING, description = "Add an annotation to this host", since = "4.11", authorized = {RoleType.Admin}) private String annotation; + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -103,6 +108,10 @@ public class UpdateHostCmd extends BaseCmd { return annotation; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java new file mode 100644 index 00000000000..803b95bfa9e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/RemoveManagementServerCmd.java @@ -0,0 +1,61 @@ +// 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.management; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.response.ManagementServerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "removeManagementServer", description = "Removes a Management Server.", responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = RoleType.Admin) +public class RemoveManagementServerCmd extends BaseCmd { + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ManagementServerResponse.class, required = true, description = "the ID of the Management Server") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + boolean result = _mgr.removeManagementServer(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove Management Server."); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } + + public String getEventType() { + return EventTypes.EVENT_MANAGEMENT_SERVER_REMOVE; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java index af3db374a7c..56333f4081b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.network; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -60,6 +61,10 @@ import static com.cloud.network.Network.Service.NetworkACL; import static com.cloud.network.Network.Service.UserData; import static com.cloud.network.Network.Service.Firewall; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisNatted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisRouted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNsxWithoutLb; + @APICommand(name = "createNetworkOffering", description = "Creates a network offering.", responseObject = NetworkOfferingResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkOfferingCmd extends BaseCmd { @@ -140,12 +145,19 @@ public class CreateNetworkOfferingCmd extends BaseCmd { description = "true if network offering is meant to be used for VPC, false otherwise.") private Boolean forVpc; + @Deprecated @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for NSX, false otherwise.", since = "4.20.0") private Boolean forNsx; + @Parameter(name = ApiConstants.PROVIDER, + type = CommandType.STRING, + description = "Name of the provider providing the service", + since = "4.21.0") + private String provider; + @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, description = "true if network offering for NSX network offering supports Load balancer service.", @@ -257,19 +269,39 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return serviceOfferingId; } + public boolean isExternalNetworkProvider() { + return Arrays.asList("NSX", "Netris").stream() + .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider)); + } + + public boolean isForNsx() { + return provider != null && provider.equalsIgnoreCase("NSX"); + } + + public boolean isForNetris() { + return provider != null && provider.equalsIgnoreCase("Netris"); + } + + public String getProvider() { + return provider; + } + public List getSupportedServices() { - if (!isForNsx()) { + if (!isExternalNetworkProvider()) { return supportedServices == null ? new ArrayList() : supportedServices; } else { List services = new ArrayList<>(List.of( Dhcp.getName(), Dns.getName(), - StaticNat.getName(), - SourceNat.getName(), - PortForwarding.getName(), UserData.getName() )); - if (getNsxSupportsLbService()) { + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + services.addAll(Arrays.asList( + StaticNat.getName(), + SourceNat.getName(), + PortForwarding.getName())); + } + if (getNsxSupportsLbService() || (provider != null && isNetrisNatted(getProvider(), getNetworkMode()))) { services.add(Lb.getName()); } if (Boolean.TRUE.equals(forVpc)) { @@ -308,10 +340,6 @@ public class CreateNetworkOfferingCmd extends BaseCmd { return forVpc; } - public boolean isForNsx() { - return BooleanUtils.isTrue(forNsx); - } - public String getNetworkMode() { return networkMode; } @@ -345,7 +373,7 @@ public class CreateNetworkOfferingCmd extends BaseCmd { public Map> getServiceProviders() { Map> serviceProviderMap = new HashMap<>(); - if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection servicesCollection = serviceProviderList.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -361,17 +389,16 @@ public class CreateNetworkOfferingCmd extends BaseCmd { providerList.add(provider); serviceProviderMap.put(service, providerList); } - } else if (Boolean.TRUE.equals(forNsx)) { - getServiceProviderMapForNsx(serviceProviderMap); + } else if (isExternalNetworkProvider()) { + getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); } return serviceProviderMap; } - private void getServiceProviderMapForNsx(Map> serviceProviderMap) { + private void getServiceProviderMapForExternalProvider(Map> serviceProviderMap, String provider) { String routerProvider = Boolean.TRUE.equals(getForVpc()) ? VirtualRouterProvider.Type.VPCVirtualRouter.name() : VirtualRouterProvider.Type.VirtualRouter.name(); - List unsupportedServices = new ArrayList<>(List.of("Vpn", "SecurityGroup", "Connectivity", - "Gateway", "BaremetalPxeService")); + List unsupportedServices = new ArrayList<>(List.of("Vpn", "Gateway", "SecurityGroup", "Connectivity", "BaremetalPxeService")); List routerSupported = List.of("Dhcp", "Dns", "UserData"); List allServices = Service.listAllServices().stream().map(Service::getName).collect(Collectors.toList()); if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) { @@ -384,9 +411,10 @@ public class CreateNetworkOfferingCmd extends BaseCmd { continue; if (routerSupported.contains(service)) serviceProviderMap.put(service, List.of(routerProvider)); - else - serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); - if (!getNsxSupportsLbService()) { + else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || NetworkACL.getName().equalsIgnoreCase(service)) { + serviceProviderMap.put(service, List.of(provider)); + } + if (isNsxWithoutLb(getProvider(), getNsxSupportsLbService()) || isNetrisRouted(getProvider(), getNetworkMode())) { serviceProviderMap.remove(Lb.getName()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 8f6d5413d72..3d20ed50a5d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -29,14 +29,17 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; import org.apache.cloudstack.api.response.VsphereStoragePoliciesResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.StringUtils; +import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; @@ -251,7 +254,40 @@ public class CreateServiceOfferingCmd extends BaseCmd { since="4.20") private Boolean purgeResources; + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, + type = CommandType.INTEGER, + description = "Number of days instance is leased for.", + since = "4.21.0") + private Integer leaseDuration; + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, + type = CommandType.UUID, + entityType = VgpuProfileResponse.class, + description = "the ID of the vGPU profile to which service offering should be mapped", + since = "4.21") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.GPU_COUNT, + type = CommandType.INTEGER, + description = "Count of GPUs to be used with this service offering. This is applicable only when passed with vGPU profile.", + since = "4.21") + private Integer gpuCount; + + @Parameter(name = ApiConstants.GPU_DISPLAY, + type = CommandType.BOOLEAN, + description = "Whether to enable GPU display for this service offering. This is applicable only when passed with vGPU profile. Defaults to false.", + since = "4.21") + private Boolean gpuDisplay; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + private Map externalDetails; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -359,9 +395,15 @@ public class CreateServiceOfferingCmd extends BaseCmd { } } } + + detailsMap.putAll(getExternalDetails()); return detailsMap; } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + public Long getRootDiskSize() { return rootDiskSize; } @@ -487,10 +529,38 @@ public class CreateServiceOfferingCmd extends BaseCmd { return false; } + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + public boolean isPurgeResources() { return Boolean.TRUE.equals(purgeResources); } + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public Boolean getGpuDisplay() { + return Boolean.TRUE.equals(gpuDisplay); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java index e94bff1fce8..fcd6b03d3e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/IsAccountAllowedToCreateOfferingsWithTagsCmd.java @@ -29,7 +29,7 @@ import org.apache.cloudstack.api.response.IsAccountAllowedToCreateOfferingsWithT responseObject = IsAccountAllowedToCreateOfferingsWithTagsResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class IsAccountAllowedToCreateOfferingsWithTagsCmd extends BaseCmd { - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "Account UUID", required = true) private Long id; @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java index e580f0d9f41..7f66ac7058a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateServiceOfferingCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.admin.offering; import java.util.ArrayList; import java.util.List; +import java.util.Map; import com.cloud.offering.ServiceOffering.State; import org.apache.cloudstack.api.APICommand; @@ -94,6 +95,12 @@ public class UpdateServiceOfferingCmd extends BaseCmd { since="4.20") private Boolean purgeResources; + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", + since = "4.21.0") + private Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -194,6 +201,10 @@ public class UpdateServiceOfferingCmd extends BaseCmd { return Boolean.TRUE.equals(purgeResources); } + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java index c1d9a6db429..36ad00e6e4a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/CreatePodCmd.java @@ -30,6 +30,8 @@ import org.apache.cloudstack.api.response.ZoneResponse; import com.cloud.dc.Pod; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "createPod", description = "Creates a new Pod.", responseObject = PodResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreatePodCmd extends BaseCmd { @@ -63,6 +65,12 @@ public class CreatePodCmd extends BaseCmd { @Parameter(name = ApiConstants.ALLOCATION_STATE, type = CommandType.STRING, description = "Allocation state of this Pod for allocation of new resources") private String allocationState; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the pod", + since = "4.21.0") + private List storageAccessGroups; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -95,6 +103,10 @@ public class CreatePodCmd extends BaseCmd { return allocationState; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -111,7 +123,7 @@ public class CreatePodCmd extends BaseCmd { @Override public void execute() { - Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState()); + Pod result = _configService.createPod(getZoneId(), getPodName(), getStartIp(), getEndIp(), getGateway(), getNetmask(), getAllocationState(), getStorageAccessGroups()); if (result != null) { PodResponse response = _responseGenerator.createPodResponse(result, false); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java index 5ad0b457ced..264e5263c4d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/pod/ListPodsByCmd.java @@ -55,6 +55,11 @@ public class ListPodsByCmd extends BaseListCmd { @Parameter(name = ApiConstants.SHOW_CAPACITIES, type = CommandType.BOOLEAN, description = "flag to display the capacity of the pods") private Boolean showCapacities; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,6 +84,18 @@ public class ListPodsByCmd extends BaseListCmd { return showCapacities; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListPodsByCmd() { + + } + + public ListPodsByCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -86,8 +103,8 @@ public class ListPodsByCmd extends BaseListCmd { @Override public void execute() { Pair, Integer> result = _mgr.searchForPods(this); - ListResponse response = new ListResponse(); - List podResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List podResponses = new ArrayList<>(); for (Pod pod : result.first()) { PodResponse podResponse = _responseGenerator.createPodResponse(pod, showCapacities); podResponse.setObjectName("pod"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java index 6b31c4cc43c..f46cecdef50 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java @@ -132,11 +132,12 @@ public class ListCapacityCmd extends BaseListCmd { Collections.sort(capacityResponses, new Comparator() { public int compare(CapacityResponse resp1, CapacityResponse resp2) { int res = resp1.getZoneName().compareTo(resp2.getZoneName()); + // Group by zone if (res != 0) { return res; - } else { - return resp1.getCapacityType().compareTo(resp2.getCapacityType()); } + // Sort by capacity type only if not already sorted by usage + return (getSortBy() != null) ? 0 : resp1.getCapacityType().compareTo(resp2.getCapacityType()); } }); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java index e0cdc0dcf80..9e34c05ce21 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.router; -import org.apache.commons.lang.BooleanUtils; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -32,7 +30,10 @@ import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.vm.VirtualMachine; @@ -86,6 +87,11 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { description = "if true is passed for this parameter, also fetch last executed health check results for the router. Default is false") private Boolean fetchHealthCheckResults; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the router", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +152,10 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { return BooleanUtils.isTrue(fetchHealthCheckResults); } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java index b779ba2a2b4..460b8d642e9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmd.java @@ -56,6 +56,9 @@ public class AddObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") private String tags; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts", since = "4.21") + private Long size; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -68,6 +71,10 @@ public class AddObjectStoragePoolCmd extends BaseCmd { return name; } + public Long getTotalSize() { + return size; + } + public Map getDetails() { Map detailsMap = null; if (details != null && !details.isEmpty()) { @@ -112,7 +119,7 @@ public class AddObjectStoragePoolCmd extends BaseCmd { @Override public void execute(){ try{ - ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getProviderName(), getDetails()); + ObjectStore result = _storageService.discoverObjectStore(getName(), getUrl(), getTotalSize(), getProviderName(), getDetails()); ObjectStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createObjectStoreResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java new file mode 100644 index 00000000000..bfa2589921f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ConfigureStorageAccessCmd.java @@ -0,0 +1,135 @@ +// 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.storage; + +import java.util.List; + +import com.cloud.event.EventTypes; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.ZoneResponse; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; + +import com.cloud.user.Account; + +@APICommand(name = "configureStorageAccess", description = "Configure the storage access groups on zone/pod/cluster/host and storage, accordingly connections to the storage pools", responseObject = SuccessResponse.class, since = "4.21.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ConfigureStorageAccessCmd extends BaseAsyncCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "UUID of the zone") + private Long zoneId; + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "UUID of the pod") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "UUID of the cluster") + private Long clusterId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "UUID of the host") + private Long hostId; + + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "UUID of the Storage Pool") + private Long storageId; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for connecting the storage pools and the hosts", + since = "4.21.0") + private List storageAccessGroups; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } + + public Long getHostId() { + return hostId; + } + + public Long getStorageId() { + return storageId; + } + + public List getStorageAccessGroups() { + return storageAccessGroups; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + try { + boolean result = _storageService.configureStorageAccess(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access"); + } + } catch (Exception e) { + logger.debug("Failed to configure storage access ", e); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure storage access, " + e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_CONFIGURE_STORAGE_ACCESS; + } + + @Override + public String getEventDescription() { + return "configuring storage access groups"; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java index 75813a7aabf..cbe4b8c06b3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/CreateStoragePoolCmd.java @@ -61,6 +61,10 @@ public class CreateStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.TAGS, type = CommandType.STRING, description = "the tags for the storage pool") private String tags; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, type = CommandType.STRING, + description = "comma separated list of storage access groups for connecting to hosts having those specific groups", since = "4.21.0") + private String storageAccessGroups; + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "the URL of the storage pool") private String url; @@ -115,6 +119,10 @@ public class CreateStoragePoolCmd extends BaseCmd { return tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + public String getUrl() { return url; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java new file mode 100644 index 00000000000..d2a1757839f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStorageAccessGroupsCmd.java @@ -0,0 +1,65 @@ +/* + * 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.storage; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listStorageAccessGroups", description = "Lists storage access groups", responseObject = StorageAccessGroupResponse.class, since = "4.21.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListStorageAccessGroupsCmd extends BaseListCmd { + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Name of the Storage access group") + private String name; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getName() { + return name; + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.StoragePool; + } + + @Override + public void execute() { + ListResponse response = _queryService.searchForStorageAccessGroups(this); + + response.setResponseName(getCommandName()); + + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java index 57a87939b6b..0f2c9f3416e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/ListStoragePoolsCmd.java @@ -41,7 +41,7 @@ public class ListStoragePoolsCmd extends BaseListCmd { @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, - description = "list storage pools belongig to the specific cluster") + description = "list storage pools belonging to the specific cluster") private Long clusterId; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the IP address for the storage pool") @@ -74,6 +74,10 @@ public class ListStoragePoolsCmd extends BaseListCmd { @Parameter(name = ApiConstants.STORAGE_CUSTOM_STATS, type = CommandType.BOOLEAN, description = "If true, lists the custom stats of the storage pool", since = "4.18.1") private Boolean customStats; + + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, description = "the name of the storage access group", since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -134,6 +138,17 @@ public class ListStoragePoolsCmd extends BaseListCmd { return customStats != null && customStats; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListStoragePoolsCmd() { + } + + public ListStoragePoolsCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java index 497179d25ef..ac007137ef1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/storage/UpdateObjectStoragePoolCmd.java @@ -44,6 +44,8 @@ public class UpdateObjectStoragePoolCmd extends BaseCmd { @Parameter(name = ApiConstants.URL, type = CommandType.STRING, description = "the url for the object store") private String url; + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the total size of the object store in GiB. Used for tracking capacity and sending alerts. Set to 0 to stop tracking.", since = "4.21") + private Long size; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -61,6 +63,10 @@ public class UpdateObjectStoragePoolCmd extends BaseCmd { return url; } + public Long getSize() { + return size; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java index e8e5ee0ebad..13113e17ea4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ListSystemVMsCmd.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.admin.systemvm; import java.util.ArrayList; import java.util.List; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -31,7 +30,9 @@ import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; @@ -74,6 +75,11 @@ public class ListSystemVMsCmd extends BaseListCmd { since = "3.0.1") private Long storageId; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the system VM", + since = "4.20.1") + private String arch; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -110,6 +116,10 @@ public class ListSystemVMsCmd extends BaseListCmd { return storageId; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 index 4f4b2631667..eafee7424ff 100644 --- 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 @@ -46,7 +46,7 @@ public class PatchSystemVMCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "If true, initiates copy of scripts and restart of the agent, even if the scripts version matches." + "To be used with ID parameter only") - private Boolean force; + private Boolean forced; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -58,7 +58,7 @@ public class PatchSystemVMCmd extends BaseAsyncCmd { } public boolean isForced() { - return force != null && force; + return forced != null && forced; } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java index 06e57674c53..d822d4fc26e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java @@ -74,7 +74,7 @@ public class ScaleSystemVMCmd extends BaseAsyncCmd { } public Map getDetails() { - return details; + return convertDetailsToMap(details); } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java index 5abe90e3f58..a42f0052249 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java @@ -68,7 +68,7 @@ public class UpgradeSystemVMCmd extends BaseCmd { } public Map getDetails() { - return details; + return convertDetailsToMap(details); } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java index c0ba99a8233..5cb384925e8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.admin.vlan; +import com.cloud.configuration.ConfigurationService; +import com.cloud.network.Network; import com.cloud.utils.net.NetUtils; import org.apache.cloudstack.api.APICommand; @@ -39,7 +41,6 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; -import java.util.Objects; @APICommand(name = "createVlanIpRange", description = "Creates a VLAN IP range.", responseObject = VlanIpRangeResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -114,8 +115,8 @@ public class CreateVlanIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") private Boolean forSystemVms; - @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if the IP range is used for NSX resource", since = "4.20.0") - private boolean forNsx; + @Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "Provider name for which the IP range is reserved for", since = "4.21.0") + private String provider; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -157,12 +158,12 @@ public class CreateVlanIpRangeCmd extends BaseCmd { return startIp; } - public boolean isForNsx() { - return !Objects.isNull(forNsx) && forNsx; + public Network.Provider getProvider() { + return Network.Provider.getProvider(provider); } public String getVlan() { - if ((vlan == null || vlan.isEmpty()) && !isForNsx()) { + if ((vlan == null || vlan.isEmpty()) && !ConfigurationService.IsIpRangeForProvider(getProvider())) { vlan = "untagged"; } return vlan; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java new file mode 100644 index 00000000000..d95f17ef304 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/CreateVMFromBackupCmdByAdmin.java @@ -0,0 +1,55 @@ +// 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.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.admin.AdminCmd; +import org.apache.cloudstack.api.command.user.vm.CreateVMFromBackupCmd; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Full, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmdByAdmin extends CreateVMFromBackupCmd implements AdminCmd { + + @Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "destination Pod ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long podId; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "destination Cluster ID to deploy the VM to - parameter available for root admin only", since = "4.21") + private Long clusterId; + + public Long getPodId() { + return podId; + } + + public Long getClusterId() { + return clusterId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java index 8881a2bc354..0bc993ef1f7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.admin.vm; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.APICommand; @@ -145,6 +146,10 @@ public class MigrateVMCmd extends BaseAsyncCmd { throw new InvalidParameterValueException("Unable to find the VM by id=" + getVirtualMachineId()); } + if (Hypervisor.HypervisorType.External.equals(userVm.getHypervisorType())) { + throw new InvalidParameterValueException("Migrate VM instance operation is not allowed for External hypervisor type"); + } + Host destinationHost = null; // OfflineMigration performed when this parameter is specified StoragePool destStoragePool = null; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index 73b4f5df196..9ed7ced528e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.admin.vpc; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -25,10 +26,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.Network; import com.cloud.network.VirtualRouterProvider; +import com.cloud.offering.NetworkOffering; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.CollectionUtils; @@ -57,6 +60,11 @@ import static com.cloud.network.Network.Service.SourceNat; import static com.cloud.network.Network.Service.PortForwarding; import static com.cloud.network.Network.Service.NetworkACL; import static com.cloud.network.Network.Service.UserData; +import static com.cloud.network.Network.Service.Gateway; + +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisNatted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNetrisRouted; +import static org.apache.cloudstack.api.command.utils.OfferingUtils.isNsxWithoutLb; @APICommand(name = "createVPCOffering", description = "Creates VPC offering", responseObject = VpcOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -112,12 +120,19 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { since = "4.13") private List zoneIds; + @Deprecated @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for NSX, false otherwise.", since = "4.20.0") private Boolean forNsx; + @Parameter(name = ApiConstants.PROVIDER, + type = CommandType.STRING, + description = "Name of the provider providing the service", + since = "4.21.0") + private String provider; + @Parameter(name = ApiConstants.NSX_SUPPORT_LB, type = CommandType.BOOLEAN, description = "true if network offering for NSX VPC offering supports Load balancer service.", @@ -158,29 +173,40 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { return StringUtils.isEmpty(displayText) ? vpcOfferingName : displayText; } + public boolean isExternalNetworkProvider() { + return Arrays.asList("NSX", "Netris").stream() + .anyMatch(s -> provider != null && s.equalsIgnoreCase(provider)); + } + public List getSupportedServices() { - if (!isForNsx() && CollectionUtils.isEmpty(supportedServices)) { + if (!isExternalNetworkProvider() && CollectionUtils.isEmpty(supportedServices)) { throw new InvalidParameterValueException("Supported services needs to be provided"); } - if (isForNsx()) { + if (isExternalNetworkProvider()) { supportedServices = new ArrayList<>(List.of( Dhcp.getName(), Dns.getName(), - StaticNat.getName(), - SourceNat.getName(), NetworkACL.getName(), - PortForwarding.getName(), UserData.getName() )); - if (getNsxSupportsLbService()) { + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + supportedServices.addAll(Arrays.asList( + StaticNat.getName(), + SourceNat.getName(), + PortForwarding.getName())); + } + if (NetworkOffering.NetworkMode.ROUTED.name().equalsIgnoreCase(getNetworkMode())) { + supportedServices.add(Gateway.getName()); + } + if (getNsxSupportsLbService() || isNetrisNatted(getProvider(), getNetworkMode())) { supportedServices.add(Lb.getName()); } } return supportedServices; } - public boolean isForNsx() { - return BooleanUtils.isTrue(forNsx); + public String getProvider() { + return provider; } public String getNetworkMode() { @@ -193,7 +219,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { public Map> getServiceProviders() { Map> serviceProviderMap = new HashMap<>(); - if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isExternalNetworkProvider()) { Collection> servicesCollection = serviceProviderList.values(); Iterator> iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -213,16 +239,18 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { providerList.add(provider); serviceProviderMap.put(service, providerList); } - } else if (Boolean.TRUE.equals(forNsx)) { - getServiceProviderMapForNsx(serviceProviderMap); + } else if (isExternalNetworkProvider()) { + getServiceProviderMapForExternalProvider(serviceProviderMap, Network.Provider.getProvider(provider).getName()); } return serviceProviderMap; } - private void getServiceProviderMapForNsx(Map> serviceProviderMap) { - List unsupportedServices = List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", - "Gateway", "Firewall"); + private void getServiceProviderMapForExternalProvider(Map> serviceProviderMap, String provider) { + List unsupportedServices = new ArrayList<>(List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", "Firewall")); + if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode())) { + unsupportedServices.add("Gateway"); + } List routerSupported = List.of("Dhcp", "Dns", "UserData"); List allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList()); for (String service : allServices) { @@ -230,10 +258,12 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { continue; if (routerSupported.contains(service)) serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name())); - else - serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); + else if (NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(getNetworkMode()) || + Stream.of(NetworkACL.getName(), Gateway.getName()).anyMatch(s -> s.equalsIgnoreCase(service))) { + serviceProviderMap.put(service, List.of(provider)); + } } - if (!getNsxSupportsLbService()) { + if ((isNsxWithoutLb(getProvider(), getNsxSupportsLbService())) || isNetrisRouted(getProvider(), getNetworkMode())) { serviceProviderMap.remove(Lb.getName()); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java index 24660e41ed9..f3c1bab260b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/zone/CreateZoneCmd.java @@ -31,6 +31,8 @@ import org.apache.cloudstack.context.CallContext; import com.cloud.dc.DataCenter; import com.cloud.user.Account; +import java.util.List; + @APICommand(name = "createZone", description = "Creates a Zone.", responseObject = ZoneResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateZoneCmd extends BaseCmd { @@ -88,6 +90,11 @@ public class CreateZoneCmd extends BaseCmd { @Parameter(name = ApiConstants.IS_EDGE, type = CommandType.BOOLEAN, description = "true if the zone is an edge zone, false otherwise", since = "4.18.0") private Boolean isEdge; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUPS, + type = CommandType.LIST, collectionType = CommandType.STRING, + description = "comma separated list of storage access groups for the hosts in the zone", + since = "4.21.0") + private List storageAccessGroups; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -162,6 +169,10 @@ public class CreateZoneCmd extends BaseCmd { return isEdge; } + public List getStorageAccessGroups() { + return storageAccessGroups; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java index 5760ca3ba1c..357f0c83ed7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java @@ -108,6 +108,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0") private Boolean forSystemVMs; + @Parameter(name = ApiConstants.FOR_PROVIDER, type = CommandType.BOOLEAN, description = "true if range is dedicated for external network provider", since = "4.21.0") + private Boolean forProvider; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -183,6 +186,10 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC return BooleanUtils.isTrue(forSystemVMs); } + public boolean isForProvider() { + return BooleanUtils.isTrue(forProvider); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java index 2d387788243..4e9028f5217 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupCmd.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.api.command.user.backup; import javax.inject.Inject; -import com.cloud.storage.Snapshot; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -28,7 +27,6 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -62,12 +60,26 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { description = "ID of the VM") private Long vmId; - @Parameter(name = ApiConstants.SCHEDULE_ID, - type = CommandType.LONG, - entityType = BackupScheduleResponse.class, - description = "backup schedule ID of the VM, if this is null, it indicates that it is a manual backup.", + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + description = "the name of the backup", since = "4.21.0") - private Long scheduleId; + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, + type = CommandType.STRING, + description = "the description for the backup", + since = "4.21.0") + private String description; + + @Parameter(name = ApiConstants.QUIESCE_VM, + type = CommandType.BOOLEAN, + required = false, + description = "Quiesce the instance before checkpointing the disks for backup. Applicable only to NAS backup provider. " + + "The filesystem is frozen before the backup starts and thawed immediately after. " + + "Requires the instance to have the QEMU Guest Agent installed and running.", + since = "4.21.0") + private Boolean quiesceVM; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -77,12 +89,16 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { return vmId; } - public Long getScheduleId() { - if (scheduleId != null) { - return scheduleId; - } else { - return Snapshot.MANUAL_POLICY_ID; - } + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Boolean getQuiesceVM() { + return quiesceVM; } ///////////////////////////////////////////////////// @@ -92,7 +108,7 @@ public class CreateBackupCmd extends BaseAsyncCreateCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.createBackup(getVmId(), getScheduleId()); + boolean result = backupManager.createBackup(this, getJob()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java index 1d0741e6217..a352c688f12 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/CreateBackupScheduleCmd.java @@ -75,12 +75,19 @@ public class CreateBackupScheduleCmd extends BaseCmd { description = "Specifies a timezone for this command. For more information on the timezone parameter, see TimeZone Format.") private String timezone; - @Parameter(name = ApiConstants.MAX_BACKUPS, - type = CommandType.INTEGER, - description = "maximum number of backups to retain", - since = "4.21.0") + @Parameter(name = ApiConstants.MAX_BACKUPS, type = CommandType.INTEGER, + since = "4.21.0", description = ApiConstants.PARAMETER_DESCRIPTION_MAX_BACKUPS) private Integer maxBackups; + @Parameter(name = ApiConstants.QUIESCE_VM, + type = CommandType.BOOLEAN, + required = false, + description = "Quiesce the instance before checkpointing the disks for backup. Applicable only to NAS backup provider. " + + "The filesystem is frozen before the backup starts and thawed immediately after. " + + "Requires the instance to have the QEMU Guest Agent installed and running.", + since = "4.21.0") + private Boolean quiesceVM; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -105,6 +112,10 @@ public class CreateBackupScheduleCmd extends BaseCmd { return maxBackups; } + public Boolean getQuiesceVM() { + return quiesceVM; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java index 0245f228b89..dbfd21d5a56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/DeleteBackupScheduleCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupScheduleResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.backup.BackupManager; @@ -42,7 +43,7 @@ import com.cloud.utils.exception.CloudRuntimeException; description = "Deletes the backup schedule of a VM", responseObject = SuccessResponse.class, since = "4.14.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class DeleteBackupScheduleCmd extends BaseCmd { +public class DeleteBackupScheduleCmd extends BaseCmd { @Inject private BackupManager backupManager; @@ -51,13 +52,15 @@ public class DeleteBackupScheduleCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, - type = CommandType.UUID, - entityType = UserVmResponse.class, - required = true, - description = "ID of the VM") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "ID of the VM from which all backup schedules will be deleted.") private Long vmId; + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = BackupScheduleResponse.class, + since = "4.20.1", description = "ID of the backup schedule to be deleted. It has precedence over the 'virtualmachineid' parameter, " + + "i.e., when the 'id' parameter is specified, the 'virtualmachineid' parameter will be ignored.") + private Long id; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +69,9 @@ public class DeleteBackupScheduleCmd extends BaseCmd { return vmId; } + public Long getId() { return id; } + + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -73,7 +79,7 @@ public class DeleteBackupScheduleCmd extends BaseCmd { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { try { - boolean result = backupManager.deleteBackupSchedule(getVmId()); + boolean result = backupManager.deleteBackupSchedule(this); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java index 7d87cc37e6c..0c2b4f9b91e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/ListBackupsCmd.java @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupOfferingResponse; import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -75,6 +76,25 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { description = "list backups by zone id") private Long zoneId; + @Parameter(name = ApiConstants.NAME, + type = CommandType.STRING, + since = "4.21.0", + description = "list backups by name") + private String name; + + @Parameter(name = ApiConstants.BACKUP_OFFERING_ID, + type = CommandType.UUID, + entityType = BackupOfferingResponse.class, + since = "4.21.0", + description = "list backups by backup offering") + private Long backupOfferingId; + + @Parameter(name = ApiConstants.LIST_VM_DETAILS, + type = CommandType.BOOLEAN, + since = "4.21.0", + description = "list backups with VM details") + private Boolean listVmDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -87,10 +107,22 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { return vmId; } + public String getName() { + return name; + } + + public Long getBackupOfferingId() { + return backupOfferingId; + } + public Long getZoneId() { return zoneId; } + public Boolean getListVmDetails() { + return listVmDetails; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -101,7 +133,7 @@ public class ListBackupsCmd extends BaseListProjectAndAccountResourcesCmd { if (backup == null) { continue; } - BackupResponse backupResponse = _responseGenerator.createBackupResponse(backup); + BackupResponse backupResponse = backupManager.createBackupResponse(backup, this.getListVmDetails()); responses.add(backupResponse); } final ListResponse response = new ListResponse<>(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index 0cecbb37020..e73bb97a21b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java @@ -72,6 +72,9 @@ public class ListCapabilitiesCmd extends BaseCmd { response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME)); response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT)); response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE)); + response.setInstanceLeaseEnabled((Boolean) capabilities.get(ApiConstants.INSTANCE_LEASE_ENABLED)); + response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH)); + response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED)); response.setObjectName("capability"); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmd.java new file mode 100644 index 00000000000..774cd9d59fe --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmd.java @@ -0,0 +1,182 @@ +// 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 org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.command.user.consoleproxy; + +import org.apache.cloudstack.consoleproxy.ConsoleSession; + +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.UserAccount; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ConsoleSessionResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.consoleproxy.ConsoleAccessManager; + +import javax.inject.Inject; +import java.util.Date; + +@APICommand(name = "listConsoleSessions", description = "Lists console sessions.", responseObject = ConsoleSessionResponse.class, + entityType = {ConsoleSession.class}, since = "4.21.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.DomainAdmin, RoleType.ResourceAdmin, RoleType.User}) +public class ListConsoleSessionsCmd extends BaseListCmd { + @Inject + private AccountService accountService; + + @Inject + private ConsoleAccessManager consoleAccessManager; + + @ACL + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ConsoleSessionResponse.class, description = "The ID of the console session.") + private Long id; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The domain ID of the account that created the console endpoint.") + private Long domainId; + + @ACL + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account that created the console endpoint.") + private Long accountId; + + @ACL + @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class, description = "The ID of the user that created the console endpoint.") + private Long userId; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, authorized = {RoleType.Admin}, description = "Lists console sessions from the specified host.") + private Long hostId; + + @Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE, description = "Lists console sessions generated from this date onwards. " + + ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS) + private Date startDate; + + @Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, description = "Lists console sessions generated up until this date. " + + ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS) + private Date endDate; + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "The ID of the virtual machine.") + private Long vmId; + + @Parameter(name = ApiConstants.CONSOLE_ENDPOINT_CREATOR_ADDRESS, type = CommandType.STRING, description = "IP address of the creator of the console endpoint.") + private String consoleEndpointCreatorAddress; + + @Parameter(name = ApiConstants.CLIENT_ADDRESS, type = CommandType.STRING, description = "IP address of the client that accessed the console session.") + private String clientAddress; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "Lists only active console sessions, defaults to true. Active sessions are the ones that have been acquired and have not been removed.") + private boolean activeOnly = true; + + @Parameter(name = ApiConstants.ACQUIRED, type = CommandType.BOOLEAN, + description = "Lists acquired console sessions, defaults to false. Acquired console sessions are the ones that have been accessed. " + + "The 'activeonly' parameter has precedence over the 'acquired' parameter, i.e., when the 'activeonly' parameter is 'true', the 'acquired' parameter value will be ignored.") + private boolean acquired = false; + + @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, + description = "Lists console sessions recursively per domain. If an account ID is informed, only the account's console sessions will be listed. Defaults to false.") + private boolean recursive = false; + + public Long getId() { + return id; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getUserId() { + return userId; + } + + public Long getHostId() { + return hostId; + } + + public Date getStartDate() { + return startDate; + } + + public Date getEndDate() { + return endDate; + } + + public Long getVmId() { + return vmId; + } + + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public String getClientAddress() { + return clientAddress; + } + + public boolean isActiveOnly() { + return activeOnly; + } + + public boolean getAcquired() { + return acquired; + } + + public boolean isRecursive() { + return recursive; + } + + @Override + public void execute() { + ListResponse response = consoleAccessManager.listConsoleSessions(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + if (getId() != null) { + ConsoleSession consoleSession = consoleAccessManager.listConsoleSessionById(getId()); + if (consoleSession != null) { + return consoleSession.getAccountId(); + } + } + + if (getAccountId() != null) { + return getAccountId(); + } + + if (getUserId() != null) { + UserAccount userAccount = accountService.getUserAccountById(getUserId()); + if (userAccount != null) { + return userAccount.getAccountId(); + } + } + + return Account.ACCOUNT_ID_SYSTEM; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java index b5273c64922..cb935c13e97 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/event/ListEventsCmd.java @@ -73,6 +73,9 @@ public class ListEventsCmd extends BaseListProjectAndAccountResourcesCmd { @Parameter(name = ApiConstants.ARCHIVED, type = CommandType.BOOLEAN, description = "true to list archived events otherwise false", since="4.19.0") private Boolean archived; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the events", since="4.21.0") + private String state; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -121,6 +124,10 @@ public class ListEventsCmd extends BaseListProjectAndAccountResourcesCmd { return archived != null && archived; } + public String getState() { + return state; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java new file mode 100644 index 00000000000..b035ecbe71b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmd.java @@ -0,0 +1,96 @@ +// 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.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; + +@APICommand(name = "listGpuCards", description = "Lists all available GPU cards", + responseObject = GpuCardResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListGpuCardsCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "ID of the GPU card") + private Long id; + + @Parameter(name = ApiConstants.VENDOR_NAME, type = CommandType.STRING, + description = "vendor name of the GPU card") + private String vendorName; + + @Parameter(name = ApiConstants.VENDOR_ID, type = CommandType.STRING, + description = "vendor ID of the GPU card") + private String vendorId; + + @Parameter(name = ApiConstants.DEVICE_ID, type = CommandType.STRING, + description = "device ID of the GPU card") + private String deviceId; + + @Parameter(name = ApiConstants.DEVICE_NAME, type = CommandType.STRING, + description = "device name of the GPU card") + private String deviceName; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only GPU cards which have a device will be listed. If false, all GPU cards will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getVendorName() { + return vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public String getDeviceId() { + return deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listGpuCards(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java new file mode 100644 index 00000000000..19c92062851 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmd.java @@ -0,0 +1,65 @@ +// 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.user.gpu; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = "listGpuDevices", description = "Lists all available GPU devices", + responseView = ResponseObject.ResponseView.Restricted, + responseObject = GpuDeviceResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListGpuDevicesCmd extends BaseListCmd implements UserCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + description = "the virtual machine ID to which the GPU device is allocated") + private Long vmId; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getVmId() { + return vmId; + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation ////////////////// + /// ////////////////////////////////////////////////// + + @Override + public void execute() { + CallContext.current().setEventDetails("Listing GPU devices"); + ListResponse response = gpuService.listGpuDevices(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java new file mode 100644 index 00000000000..85bf91d7aee --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmd.java @@ -0,0 +1,80 @@ +// 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.user.gpu; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; + +@APICommand(name = "listVgpuProfiles", description = "Lists all available vGPU profiles", + responseObject = VgpuProfileResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0") +public class ListVgpuProfilesCmd extends BaseListCmd { + + /// ////////////////////////////////////////////////// + /// ///////////// API parameters ///////////////////// + /// ////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VgpuProfileResponse.class, + description = "ID of the vGPU profile") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the vGPU profile") + private String name; + + @Parameter(name = ApiConstants.GPU_CARD_ID, type = CommandType.UUID, entityType = GpuCardResponse.class, + description = "the GPU card ID associated with this GPU device") + private Long cardId; + + @Parameter(name = ApiConstants.ACTIVE_ONLY, type = CommandType.BOOLEAN, + description = "If true, only vGPU profiles which have a device will be listed. If false, all vGPU profiles will be listed.") + private Boolean activeOnly; + + /// ////////////////////////////////////////////////// + /// //////////////// Accessors /////////////////////// + /// ////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getCardId() { + return cardId; + } + + public boolean getActiveOnly() { + return Boolean.TRUE.equals(activeOnly); + } + + /// ////////////////////////////////////////////////// + /// //////////// API Implementation/////////////////// + /// ////////////////////////////////////////////////// + + @Override public void execute() { + ListResponse response = gpuService.listVgpuProfiles(this); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java index c74514d662c..413798970bf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmd.java @@ -26,7 +26,9 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import com.cloud.cpu.CPU; import com.cloud.storage.GuestOsCategory; import com.cloud.utils.Pair; @@ -39,12 +41,48 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "list Os category by id") + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, description = "List OS category by id") private Long id; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "list os category by name", since = "3.0.1") + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "List OS category by name", since = "3.0.1") private String name; + @Parameter(name = ApiConstants.IS_FEATURED, + type = CommandType.BOOLEAN, + description = "List available OS categories by featured or not", + since = "4.21.0") + private Boolean featured; + + @Parameter(name = ApiConstants.IS_ISO, + type = CommandType.BOOLEAN, + description = "List OS categories for which an ISO is available", + since = "4.21.0") + private Boolean iso; + + @Parameter(name = ApiConstants.IS_VNF, type = CommandType.BOOLEAN, + description = "List OS categories for which a VNF template is available", + since = "4.21.0") + private Boolean vnf; + + @Parameter(name = ApiConstants.ZONE_ID, + type = CommandType.UUID, + entityType = ZoneResponse.class, + description = "List available OS categories types for the zone", + since = "4.21.0") + private Long zoneId; + + @Parameter(name = ApiConstants.ARCH, + type = CommandType.STRING, + description = "List OS categories types available for given CPU architecture", + since = "4.21.0") + private String arch; + + @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, + type = CommandType.BOOLEAN, + description = "flag to display the resource image for the OS category", + since = "4.21.0") + private Boolean showIcon; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -57,6 +95,30 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { return name; } + public Boolean isFeatured() { + return featured; + } + + public Boolean isIso() { + return iso; + } + + public Boolean isVnf() { + return vnf; + } + + public Long getZoneId() { + return zoneId; + } + + public CPU.CPUArch getArch() { + return arch == null ? null : CPU.CPUArch.fromType(arch); + } + + public boolean isShowIcon() { + return Boolean.TRUE.equals(showIcon); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -64,14 +126,11 @@ public class ListGuestOsCategoriesCmd extends BaseListCmd { @Override public void execute() { Pair, Integer> result = _mgr.listGuestOSCategoriesByCriteria(this); - ListResponse response = new ListResponse(); - List osCatResponses = new ArrayList(); + ListResponse response = new ListResponse<>(); + List osCatResponses = new ArrayList<>(); for (GuestOsCategory osCategory : result.first()) { - GuestOSCategoryResponse categoryResponse = new GuestOSCategoryResponse(); - categoryResponse.setId(osCategory.getUuid()); - categoryResponse.setName(osCategory.getName()); - - categoryResponse.setObjectName("oscategory"); + GuestOSCategoryResponse categoryResponse = _responseGenerator.createGuestOSCategoryResponse(osCategory, + isShowIcon()); osCatResponses.add(categoryResponse); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java new file mode 100644 index 00000000000..8566b413cc1 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/CreateGuiThemeCmd.java @@ -0,0 +1,129 @@ +// 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.user.gui.theme; + +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "createGuiTheme", description = "Creates a customized GUI theme for a set of Common Names (fixed or wildcard), a set of domain UUIDs, and/or a set of " + + "account UUIDs.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, + since = "4.21.0.0", authorized = {RoleType.Admin}) +public class CreateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.NAME, required = true, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.createGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java new file mode 100644 index 00000000000..35a0a749aa9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/ListGuiThemesCmd.java @@ -0,0 +1,110 @@ +// 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.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "listGuiThemes", description = "Lists GUI themes.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.User, RoleType.DomainAdmin, + RoleType.ResourceAdmin}) +public class ListGuiThemesCmd extends BaseListCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, description = "The theme ID.", entityType = GuiThemeResponse.class) + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the theme.") + private String name; + + @Parameter(name = ApiConstants.COMMON_NAME, type = CommandType.STRING, description = "The internet Common Name (CN) to be filtered.") + private String commonName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "The ID of the domain to be filtered.") + private Long domainId; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "The ID of the account to be filtered.") + private Long accountId; + + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "Whether to list all themes.") + private boolean listAll = false; + + @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "Whether to list removed themes.") + private boolean showRemoved = false; + + @Parameter(name = ApiConstants.SHOW_PUBLIC, type = CommandType.BOOLEAN, description = "Whether to list public themes.") + private Boolean showPublic; + + @Parameter(name = ApiConstants.LIST_ONLY_DEFAULT_THEME, type = CommandType.BOOLEAN, description = "Whether to only list the default theme.") + private boolean listOnlyDefaultTheme = false; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCommonName() { + return commonName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getAccountId() { + return accountId; + } + + public boolean getListAll() { + return listAll; + } + + public boolean getShowRemoved() { + return showRemoved; + } + + public Boolean getShowPublic() { + return showPublic; + } + + public boolean getListOnlyDefaultTheme() { + return listOnlyDefaultTheme; + } + + @Override + public void execute() { + ListResponse response = guiThemeService.listGuiThemes(this); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java new file mode 100644 index 00000000000..64164838eba --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/RemoveGuiThemeCmd.java @@ -0,0 +1,60 @@ +// 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.user.gui.theme; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + +@APICommand(name = "removeGuiTheme", description = "Removes an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class RemoveGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuiThemeResponse.class, required = true, + description = "The unique identifier of the GUI theme to be removed.") + private Long id; + + public Long getId() { + return id; + } + + @Override + public void execute() { + guiThemeService.removeGuiTheme(this); + final SuccessResponse response = new SuccessResponse(); + response.setResponseName(getCommandName()); + response.setSuccess(true); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java new file mode 100644 index 00000000000..daef2235ce8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/gui/theme/UpdateGuiThemeCmd.java @@ -0,0 +1,136 @@ +// 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.user.gui.theme; + +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.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuiThemeResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.gui.theme.GuiTheme; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; +import org.apache.cloudstack.gui.theme.GuiThemeService; + +import javax.inject.Inject; + + +@APICommand(name = "updateGuiTheme", description = "Updates an existing GUI theme.", responseObject = GuiThemeResponse.class, entityType = {GuiTheme.class}, + since = "4.21.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin}) +public class UpdateGuiThemeCmd extends BaseCmd { + + @Inject + GuiThemeService guiThemeService; + + @Parameter(name = ApiConstants.ID, required = true, type = CommandType.UUID, entityType = GuiThemeResponse.class, description = "The ID of the theme to be updated.") + private Long id; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, length = 2048, description = "A name to identify the theme.") + private String name; + + @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING, length = 4096, description = "A description for the theme.") + private String description; + + @Parameter(name = ApiConstants.CSS, type = CommandType.STRING, length = 65535, description = "The CSS to be retrieved and imported into the GUI " + + "when matching the theme access configurations.") + private String css; + + @Parameter(name = ApiConstants.JSON_CONFIGURATION, type = CommandType.STRING, length = 65535, description = "The JSON with the configurations to be " + + "retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @Parameter(name = ApiConstants.COMMON_NAMES, type = CommandType.STRING, length = 65535, description = "A set of Common Names (CN) (fixed or " + + "wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @Parameter(name = ApiConstants.DOMAIN_IDS, type = CommandType.STRING, length = 65535, description = "A set of domain UUIDs (also known as ID for " + + "the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @Parameter(name = ApiConstants.RECURSIVE_DOMAINS, type = CommandType.BOOLEAN, description = "Defines whether the subdomains of the informed domains are considered. Default " + + "value is false.") + private Boolean recursiveDomains = false; + + @Parameter(name = ApiConstants.ACCOUNT_IDS, type = CommandType.STRING, length = 65535, description = "A set of account UUIDs (also known as ID for" + + " the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "Defines whether a theme can be retrieved by anyone when only " + + "the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is considered as `false`.") + private Boolean isPublic = true; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getCss() { + return css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public Boolean getIsPublic() { + return isPublic; + } + + @Override + public void execute() { + GuiThemeJoin guiThemeJoin = guiThemeService.updateGuiTheme(this); + + if (guiThemeJoin == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update the GUI theme."); + } + + GuiThemeResponse response = _responseGenerator.createGuiThemeResponse(guiThemeJoin); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java index 292e1c6f099..78f1a4bcdee 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java @@ -104,7 +104,7 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId, isForced()); + boolean result = _templateService.detachIso(virtualMachineId, null, isForced()); if (result) { UserVm userVm = _entityMgr.findById(UserVm.class, virtualMachineId); UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", userVm).get(0); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java index 7861c1e5d41..4d11f0dc3d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ExtractIsoCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.iso; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -101,7 +102,15 @@ public class ExtractIsoCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "extracting ISO: " + getId() + " from zone: " + getZoneId(); + String isoId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); + String baseDescription = String.format("Extracting ISO: %s", isoId); + + Long zoneId = getZoneId(); + if (zoneId == null) { + return baseDescription; + } + + return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 5c4d606a93c..346eca8cff0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -16,11 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.iso; -import com.cloud.cpu.CPU; -import com.cloud.server.ResourceIcon; -import com.cloud.server.ResourceTag; -import org.apache.cloudstack.api.response.ResourceIconResponse; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -28,16 +23,17 @@ import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; - -import com.cloud.template.VirtualMachineTemplate.TemplateFilter; -import com.cloud.user.Account; import org.apache.commons.lang3.StringUtils; -import java.util.List; +import com.cloud.cpu.CPU; +import com.cloud.server.ResourceTag; +import com.cloud.template.VirtualMachineTemplate.TemplateFilter; +import com.cloud.user.Account; @APICommand(name = "listIsos", description = "Lists all available ISO files.", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -61,7 +57,7 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name = ApiConstants.IS_PUBLIC, type = CommandType.BOOLEAN, description = "true if the ISO is publicly available to all users, false otherwise.") private Boolean publicIso; - @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "true if this ISO is ready to be deployed") + @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "list ISOs that are ready to be deployed") private Boolean ready; @Parameter(name = ApiConstants.ISO_FILTER, @@ -95,6 +91,11 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { since = "4.20") private String arch; + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType= GuestOSCategoryResponse.class, + description = "the ID of the OS category for the ISO", + since = "4.21.0") + private Long osCategoryId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -173,6 +174,10 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { return CPU.CPUArch.fromType(arch); } + public Long getOsCategoryId() { + return osCategoryId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -190,24 +195,14 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Override public void execute() { ListResponse response = _queryService.listIsos(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateIsoResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.ISO); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateIsoResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.ISO, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public Long getStoragePoolId() { return null; }; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java index 4f9d2f37d13..bfc15546840 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.SslCertResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.commons.lang3.BooleanUtils; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; @@ -57,11 +58,17 @@ public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd { description = "the ID of the certificate") Long certId; + @Parameter(name = ApiConstants.FORCED, + type = CommandType.BOOLEAN, + since = "4.22", + description = "Force assign the certificate. If there is a certificate assigned to the LB, it will be removed at first.") + private Boolean forced; + @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { //To change body of implemented methods use File | Settings | File Templates. - if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId())) { + if (_lbService.assignCertToLoadBalancer(getLbRuleId(), getCertId(), isForced())) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { @@ -95,4 +102,19 @@ public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd { public Long getLbRuleId() { return lbRuleId; } + + public boolean isForced() { + return BooleanUtils.toBoolean(forced); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + return (lb != null)? lb.getNetworkId(): null; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java index 34798c4efe1..aa43b9cfdaf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.lang3.StringUtils; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -112,7 +113,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L + "rule will be created for. Required when public Ip address is not associated with any Guest network yet (VPC case)") private Long networkId; - @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB such as tcp, udp or tcp-proxy.") + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB such as tcp, udp, tcp-proxy or ssl.") private String lbProtocol; @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the rule to the end user or not", since = "4.4", authorized = {RoleType.Admin}) @@ -253,7 +254,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements L } public String getLbProtocol() { - return lbProtocol; + return StringUtils.trim(StringUtils.lowerCase(lbProtocol)); } ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java index dfaafe89923..ddd2133d932 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java @@ -82,4 +82,15 @@ public class RemoveCertFromLoadBalancerCmd extends BaseAsyncCmd { public Long getLbRuleId() { return this.lbRuleId; } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + return (lb != null)? lb.getNetworkId(): null; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerRuleCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerRuleCmd.java index 25254ba9eb7..8593cad82c6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerRuleCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/loadbalancer/UpdateLoadBalancerRuleCmd.java @@ -33,6 +33,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LoadBalancer; import com.cloud.user.Account; +import java.util.List; @APICommand(name = "updateLoadBalancerRule", description = "Updates load balancer", responseObject = LoadBalancerResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -64,6 +65,9 @@ public class UpdateLoadBalancerRuleCmd extends BaseAsyncCustomIdCmd { @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, description = "The protocol for the LB") private String lbProtocol; + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from", since = "4.22") + private List cidrList; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -92,6 +96,9 @@ public class UpdateLoadBalancerRuleCmd extends BaseAsyncCustomIdCmd { return lbProtocol; } + public List getCidrList() { + return cidrList; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java index 1b3f531e370..3b693fe57b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; @@ -110,6 +111,19 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC since = "4.20.0") private Long templateId; + @Parameter(name = ApiConstants.VGPU_PROFILE_ID, + type = CommandType.UUID, + entityType = VgpuProfileResponse.class, + description = "The ID of the vGPU profile that listed offerings must support", + since = "4.21.0") + private Long vgpuProfileId; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the service offering supports GPU. If set to true, only service offerings that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -171,6 +185,14 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC return templateId; } + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmd.java index 07973fcbfca..ac54ebbd8f8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmd.java @@ -17,9 +17,13 @@ package org.apache.cloudstack.api.command.user.snapshot; -import java.util.ArrayList; -import java.util.List; - +import com.cloud.dc.DataCenter; +import com.cloud.event.EventTypes; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.StorageUnavailableException; +import com.cloud.storage.Snapshot; +import com.cloud.user.Account; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -31,26 +35,24 @@ import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.commons.collections.CollectionUtils; - -import com.cloud.dc.DataCenter; -import com.cloud.event.EventTypes; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.StorageUnavailableException; -import com.cloud.storage.Snapshot; -import com.cloud.user.Account; +import org.apache.commons.lang3.BooleanUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; +import java.util.List; + @APICommand(name = "copySnapshot", description = "Copies a snapshot from one zone to another.", responseObject = SnapshotResponse.class, responseView = ResponseObject.ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.19.0", authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { public static final Logger logger = LogManager.getLogger(CopySnapshotCmd.class.getName()); + private Snapshot snapshot; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -84,6 +86,20 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { "Do not specify destzoneid and destzoneids together, however one of them is required.") protected List destZoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + required = false, + authorized = RoleType.Admin, + since = "4.21.0", + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present. Currently supported for StorPool only") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, since = "4.21.0", description = "This parameter enables the option the snapshot to be copied to supported primary storage") + protected Boolean useStorageReplication; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -106,7 +122,15 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { destIds.add(destZoneId); return destIds; } - return null; + return new ArrayList<>(); + } + + public List getStoragePoolIds() { + return storagePoolIds; + } + + public Boolean useStorageReplication() { + return BooleanUtils.toBoolean(useStorageReplication); } @Override @@ -152,7 +176,7 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { @Override public void execute() throws ResourceUnavailableException { try { - if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds)) + if (destZoneId == null && CollectionUtils.isEmpty(destZoneIds) && useStorageReplication()) throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Either destzoneid or destzoneids parameters have to be specified."); @@ -161,7 +185,7 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { "Both destzoneid and destzoneids cannot be specified at the same time."); CallContext.current().setEventDetails(getEventDescription()); - Snapshot snapshot = _snapshotService.copySnapshot(this); + snapshot = _snapshotService.copySnapshot(this); if (snapshot != null) { SnapshotResponse response = _queryService.listSnapshot(this); @@ -177,6 +201,13 @@ public class CopySnapshotCmd extends BaseAsyncCmd implements UserCmd { logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); } + } + public Snapshot getSnapshot() { + return snapshot; + } + + public void setSnapshot(Snapshot snapshot) { + this.snapshot = snapshot; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 3289ac2fe10..60f3bbda858 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -16,11 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.user.snapshot; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -32,6 +34,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.SnapshotPolicyResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.MapUtils; @@ -99,6 +102,19 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { since = "4.19.0") protected List zoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + authorized = RoleType.Admin, + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present.", + since = "4.21.0") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, description = "This parameter enables the option the snapshot to be copied to supported primary storage") + protected Boolean useStorageReplication; + private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject; // /////////////////////////////////////////////////// @@ -161,6 +177,17 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { return zoneIds; } + public List getStoragePoolIds() { + return storagePoolIds == null ? new ArrayList<>() : storagePoolIds; + } + + public Boolean useStorageReplication() { + if (useStorageReplication == null) { + return false; + } + return useStorageReplication; + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// @@ -209,7 +236,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds()); + Snapshot snapshot = _volumeService.allocSnapshot(getVolumeId(), getPolicyId(), getSnapshotName(), getLocationType(), getZoneIds(), getStoragePoolIds(), useStorageReplication()); if (snapshot != null) { setEntityId(snapshot.getId()); setEntityUuid(snapshot.getUuid()); @@ -223,7 +250,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { Snapshot snapshot; try { snapshot = - _volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds()); + _volumeService.takeSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm(), getLocationType(), getAsyncBackup(), getTags(), getZoneIds(), getStoragePoolIds(), useStorageReplication()); if (snapshot != null) { SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); @@ -243,7 +270,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { } } - private Snapshot.LocationType getLocationType() { + public Snapshot.LocationType getLocationType() { if (Snapshot.LocationType.values() == null || Snapshot.LocationType.values().length == 0 || locationType == null) { return null; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java index cdd908dfb87..c99373688e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotFromVMSnapshotCmd.java @@ -153,7 +153,7 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName()); + Snapshot snapshot = this._volumeService.allocSnapshotForVm(getVmId(), getVolumeId(), getSnapshotName(), getVMSnapshotId()); if (snapshot != null) { this.setEntityId(snapshot.getId()); this.setEntityUuid(snapshot.getUuid()); @@ -209,4 +209,9 @@ public class CreateSnapshotFromVMSnapshotCmd extends BaseAsyncCreateCmd { } return null; } + + @Override + public Long getApiResourceId() { + return getEntityId(); + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java index e30b897db2e..66089894737 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotPolicyCmd.java @@ -16,11 +16,13 @@ // under the License. package org.apache.cloudstack.api.command.user.snapshot; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.projects.Project; +import com.cloud.storage.Volume; +import com.cloud.storage.snapshot.SnapshotPolicy; +import com.cloud.user.Account; +import java.util.ArrayList; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -30,16 +32,16 @@ import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.SnapshotPolicyResponse; +import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.MapUtils; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.projects.Project; -import com.cloud.storage.Volume; -import com.cloud.storage.snapshot.SnapshotPolicy; -import com.cloud.user.Account; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.commons.lang3.BooleanUtils; @APICommand(name = "createSnapshotPolicy", description = "Creates a snapshot policy for the account.", responseObject = SnapshotPolicyResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -83,6 +85,17 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { "The snapshots will always be made available in the zone in which the volume is present.") protected List zoneIds; + @Parameter(name = ApiConstants.STORAGE_ID_LIST, + type=CommandType.LIST, + collectionType = CommandType.UUID, + entityType = StoragePoolResponse.class, + description = "A comma-separated list of IDs of the storage pools in other zones in which the snapshot will be made available. " + + "The snapshot will always be made available in the zone in which the volume is present.", + since = "4.21.0") + protected List storagePoolIds; + + @Parameter (name = ApiConstants.USE_STORAGE_REPLICATION, type=CommandType.BOOLEAN, required = false, since = "4.21.0", description = "This parameter enables the option the snapshot to be copied to supported primary storage") + protected Boolean useStorageReplication; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -119,6 +132,14 @@ public class CreateSnapshotPolicyCmd extends BaseCmd { return zoneIds; } + public List getStoragePoolIds() { + return storagePoolIds == null ? new ArrayList<>() : storagePoolIds; + } + + public Boolean useStorageReplication() { + return BooleanUtils.toBoolean(useStorageReplication); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java index 0a7bf291843..5f09ac6698d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/CreateTemplateCmd.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; @@ -148,6 +149,11 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { since = "4.19.0") private String accountName; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "the CPU arch of the template. Valid options are: x86_64, aarch64. Defaults to x86_64", + since = "4.20.2") + private String arch; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -234,6 +240,10 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd { return accountName; } + public CPU.CPUArch getArch() { + return CPU.CPUArch.fromType(arch); + } + // /////////////////////////////////////////////////// // ///////////// API Implementation/////////////////// // /////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java index 0fa0679bfd9..22f59351e9a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ExtractTemplateCmd.java @@ -101,7 +101,15 @@ public class ExtractTemplateCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "extracting template: " + this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()) + ((getZoneId() != null) ? " from zone: " + this._uuidMgr.getUuid(DataCenter.class, getZoneId()) : ""); + String templateId = this._uuidMgr.getUuid(VirtualMachineTemplate.class, getId()); + String baseDescription = String.format("Extracting template: %s", templateId); + + Long zoneId = getZoneId(); + if (zoneId == null) { + return baseDescription; + } + + return String.format("%s from zone: %s", baseDescription, this._uuidMgr.getUuid(DataCenter.class, zoneId)); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java index 8fa1a5d53eb..330224a6055 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/GetUploadParamsForTemplateCmd.java @@ -99,6 +99,11 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") private Boolean deployAsIs; + @Parameter(name=ApiConstants.FOR_CKS, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + public String getDisplayText() { return StringUtils.isBlank(displayText) ? getName() : displayText; } @@ -168,6 +173,10 @@ public class GetUploadParamsForTemplateCmd extends AbstractGetUploadParamsCmd { Boolean.TRUE.equals(deployAsIs); } + public boolean isForCks() { + return Boolean.TRUE.equals(forCks); + } + public CPU.CPUArch getArch() { return CPU.CPUArch.fromType(arch); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index bff65ef70a9..223ac57b11f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -16,17 +16,11 @@ // under the License. package org.apache.cloudstack.api.command.user.template; -import com.cloud.cpu.CPU; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.server.ResourceIcon; -import com.cloud.server.ResourceTag; -import org.apache.cloudstack.api.response.ResourceIconResponse; -import org.apache.commons.collections.CollectionUtils; - import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; + import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -34,15 +28,21 @@ import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.ExtensionResponse; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.server.ResourceTag; import com.cloud.template.VirtualMachineTemplate; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; import com.cloud.user.Account; -import org.apache.commons.lang3.StringUtils; @APICommand(name = "listTemplates", description = "List all public, private, and privileged templates.", responseObject = TemplateResponse.class, entityType = {VirtualMachineTemplate.class}, responseView = ResponseView.Restricted, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) @@ -106,11 +106,29 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User since = "4.19.0") private Boolean isVnf; + @Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN, + description = "list templates that can be used to deploy CKS clusters", + since = "4.21.0") + private Boolean forCks; + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, description = "the CPU arch of the template. Valid options are: x86_64, aarch64", since = "4.20") private String arch; + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, + description = "the ID of the OS category for the template", + since = "4.21.0") + private Long osCategoryId; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "ID of the extension for the template", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.IS_READY, type = CommandType.BOOLEAN, description = "list templates that are ready to be deployed", since = "4.21.0") + private Boolean ready; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -180,6 +198,13 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User boolean onlyReady = (templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.selfexecutable) || (templateFilter == TemplateFilter.sharedexecutable) || (templateFilter == TemplateFilter.executable && isAccountSpecific) || (templateFilter == TemplateFilter.community); + + if (!onlyReady) { + if (isReady() != null && isReady().booleanValue() != onlyReady) { + onlyReady = isReady().booleanValue(); + } + } + return onlyReady; } @@ -198,6 +223,8 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User return isVnf; } + public Boolean getForCks() { return forCks; } + public CPU.CPUArch getArch() { if (StringUtils.isBlank(arch)) { return null; @@ -205,6 +232,18 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User return CPU.CPUArch.fromType(arch); } + public Long getOsCategoryId() { + return osCategoryId; + } + + public Long getExtensionId() { + return extensionId; + } + + public Boolean isReady() { + return ready; + } + @Override public String getCommandName() { return s_name; @@ -218,24 +257,14 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User @Override public void execute() { ListResponse response = _queryService.listTemplates(this); - if (response != null && response.getCount() > 0 && getShowIcon()) { - updateTemplateResponse(response.getResponses()); + if (response != null && getShowIcon()) { + _responseGenerator.updateTemplateIsoResponsesForIcons(response.getResponses(), + ResourceTag.ResourceObjectType.Template); } response.setResponseName(getCommandName()); setResponseObject(response); } - private void updateTemplateResponse(List response) { - for (TemplateResponse templateResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Template, templateResponse.getId()); - if (resourceIcon == null) { - continue; - } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); - templateResponse.setResourceIconResponse(iconResponse); - } - } - public List getIds() { if (ids == null) { return Collections.emptyList(); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 1f968b869b9..5d5cab219c1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -16,15 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.user.template; -import com.cloud.cpu.CPU; -import com.cloud.hypervisor.Hypervisor; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; -import com.cloud.hypervisor.HypervisorGuru; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; @@ -35,6 +32,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ProjectResponse; @@ -43,7 +41,10 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.ResourceAllocationException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.HypervisorGuru; import com.cloud.template.VirtualMachineTemplate; @APICommand(name = "registerTemplate", description = "Registers an existing template into the CloudStack cloud. ", responseObject = TemplateResponse.class, responseView = ResponseView.Restricted, @@ -168,6 +169,11 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { description = "(VMware only) true if VM deployments should preserve all the configurations defined for this template", since = "4.15.1") protected Boolean deployAsIs; + @Parameter(name=ApiConstants.FOR_CKS, + type = CommandType.BOOLEAN, + description = "if true, the templates would be available for deploying CKS clusters", since = "4.21.0") + protected Boolean forCks; + @Parameter(name = ApiConstants.TEMPLATE_TYPE, type = CommandType.STRING, description = "the type of the template. Valid options are: USER/VNF (for all users) and SYSTEM/ROUTING/BUILTIN (for admins only).", since = "4.19.0") @@ -178,6 +184,14 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { since = "4.20") private String arch; + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, entityType = ExtensionResponse.class, + description = "ID of the extension", + since = "4.21.0") + private Long extensionId; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, type = CommandType.MAP, description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].endpoint.url=urlvalue", since = "4.21.0") + protected Map externalDetails; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -295,6 +309,10 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { Boolean.TRUE.equals(deployAsIs); } + public boolean isForCks() { + return Boolean.TRUE.equals(forCks); + } + public String getTemplateType() { return templateType; } @@ -303,6 +321,14 @@ public class RegisterTemplateCmd extends BaseCmd implements UserCmd { return CPU.CPUArch.fromType(arch); } + public Long getExtensionId() { + return extensionId; + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java index dbbd771293a..20849d1ba6c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/UpdateTemplateCmd.java @@ -46,6 +46,11 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use @Parameter(name = ApiConstants.TEMPLATE_TAG, type = CommandType.STRING, description = "the tag for this template.", since = "4.20.0") private String templateTag; + @Parameter(name = ApiConstants.FOR_CKS, type = CommandType.BOOLEAN, + description = "indicates that the template can be used for deployment of CKS clusters", + since = "4.21.0") + private Boolean forCks; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -63,6 +68,10 @@ public class UpdateTemplateCmd extends BaseUpdateTemplateOrIsoCmd implements Use return templateTag; } + public Boolean getForCks() { + return forCks; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java new file mode 100644 index 00000000000..c002bd226a0 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/BaseRegisterUserDataCmd.java @@ -0,0 +1,87 @@ +// 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.user.userdata; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkModel; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public abstract class BaseRegisterUserDataCmd extends BaseCmd { + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the user data") + private String name; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the user data. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, + type = CommandType.UUID, + entityType = DomainResponse.class, + description = "an optional domainId for the user data. If the account parameter is used, domainId must also be used.") + private Long domainId; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the user data") + private Long projectId; + + @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in user data content") + private String params; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getName() { + return name; + } + + public String getAccountName() { + return accountName; + } + + public Long getDomainId() { + return domainId; + } + + public Long getProjectId() { + return projectId; + } + + public String getParams() { + checkForVRMetadataFileNames(params); + return params; + } + + public void checkForVRMetadataFileNames(String params) { + if (StringUtils.isNotEmpty(params)) { + List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); + keyValuePairs.retainAll(NetworkModel.metadataFileNames); + if (!keyValuePairs.isEmpty()) { + throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java new file mode 100644 index 00000000000..8faa612d3d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteCniConfigurationCmd.java @@ -0,0 +1,74 @@ +// 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.user.userdata; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.user.Account; +import com.cloud.user.UserData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +@APICommand(name = "deleteCniConfiguration", description = "Deletes a CNI Configuration", responseObject = SuccessResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class DeleteCniConfigurationCmd extends DeleteUserDataCmd { + + public static final Logger logger = LogManager.getLogger(DeleteCniConfigurationCmd.class.getName()); + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + boolean result = _mgr.deleteCniConfiguration(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setSuccess(result); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete CNI configuration"); + } + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + Long domainId = this.getDomainId(); + String accountName = this.getAccountName(); + if ((account == null || _accountService.isAdmin(account.getId())) && (domainId != null && accountName != null)) { + Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId); + if (userAccount != null) { + return userAccount.getId(); + } + } + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java new file mode 100644 index 00000000000..3a0c3f1168b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListCniConfigurationCmd.java @@ -0,0 +1,59 @@ +// 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.user.userdata; + +import com.cloud.user.UserData; +import com.cloud.utils.Pair; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +@APICommand(name = "listCniConfiguration", description = "List user data for CNI plugins", responseObject = UserDataResponse.class, entityType = {UserData.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class ListCniConfigurationCmd extends ListUserDataCmd { + public static final Logger logger = LogManager.getLogger(ListCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + Pair, Integer> resultList = _mgr.listUserDatas(this, true); + List responses = new ArrayList<>(); + for (UserData result : resultList.first()) { + UserDataResponse r = _responseGenerator.createUserDataResponse(result); + r.setObjectName(ApiConstants.CNI_CONFIG); + responses.add(r); + } + + ListResponse response = new ListResponse<>(); + response.setResponses(responses, resultList.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + + +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java index 64ab3ec3d70..16bf1e5c1e4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmd.java @@ -61,7 +61,7 @@ public class ListUserDataCmd extends BaseListProjectAndAccountResourcesCmd { @Override public void execute() { - Pair, Integer> resultList = _mgr.listUserDatas(this); + Pair, Integer> resultList = _mgr.listUserDatas(this, false); List responses = new ArrayList<>(); for (UserData result : resultList.first()) { UserDataResponse r = _responseGenerator.createUserDataResponse(result); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java new file mode 100644 index 00000000000..eb80da3be05 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterCniConfigurationCmd.java @@ -0,0 +1,77 @@ +// 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.user.userdata; + +import com.cloud.user.UserData; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@APICommand(name = "registerCniConfiguration", + description = "Register a CNI Configuration to be used with CKS cluster", + since = "4.21.0", + responseObject = SuccessResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class RegisterCniConfigurationCmd extends BaseRegisterUserDataCmd { + public static final Logger logger = LogManager.getLogger(RegisterCniConfigurationCmd.class.getName()); + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.CNI_CONFIG, type = CommandType.STRING, description = "CNI Configuration content to be registered as User data", length = 1048576) + private String cniConfig; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getCniConfig() { + return cniConfig; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + UserData result = _mgr.registerCniConfiguration(this); + UserDataResponse response = _responseGenerator.createUserDataResponse(result); + response.setResponseName(getCommandName()); + response.setObjectName(ApiConstants.CNI_CONFIG); + setResponseObject(response); + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java index 41d865d678c..185c9c05b1c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java @@ -16,122 +16,50 @@ // under the License. package org.apache.cloudstack.api.command.user.userdata; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.lang3.StringUtils; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.network.NetworkModel; import com.cloud.user.UserData; @APICommand(name = "registerUserData", - description = "Register a new userdata.", + description = "Register a new User Data.", since = "4.18", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) -public class RegisterUserDataCmd extends BaseCmd { +public class RegisterUserDataCmd extends BaseRegisterUserDataCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "Name of the userdata") - private String name; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the userdata. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.UUID, - entityType = DomainResponse.class, - description = "an optional domainId for the userdata. If the account parameter is used, domainId must also be used.") - private Long domainId; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "an optional project for the userdata") - private Long projectId; - - @Parameter(name = ApiConstants.USER_DATA, - type = CommandType.STRING, - required = true, - description = "Base64 encoded userdata content. " + - "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " + - "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + - "You also need to change vm.userdata.max.length value", - length = 1048576) - private String userData; - - @Parameter(name = ApiConstants.PARAMS, type = CommandType.STRING, description = "comma separated list of variables declared in userdata content") - private String params; - - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public String getName() { - return name; - } - - public String getAccountName() { - return accountName; - } - - public Long getDomainId() { - return domainId; - } - - public Long getProjectId() { - return projectId; - } + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, required = true, description = "User data content", length = 1048576) + protected String userData; public String getUserData() { return userData; } - public String getParams() { - checkForVRMetadataFileNames(params); - return params; - } - - public void checkForVRMetadataFileNames(String params) { - if (StringUtils.isNotEmpty(params)) { - List keyValuePairs = new ArrayList<>(Arrays.asList(params.split(","))); - keyValuePairs.retainAll(NetworkModel.metadataFileNames); - if (!keyValuePairs.isEmpty()) { - throw new InvalidParameterValueException(String.format("Params passed here have a few virtual router metadata file names %s", keyValuePairs)); - } - } - } - ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @Override public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(getAccountName(), getDomainId(), getProjectId(), true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java new file mode 100644 index 00000000000..ecbde47692f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -0,0 +1,848 @@ +// 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.user.vm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.Nonnull; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.ApiArgValidator; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import org.apache.cloudstack.api.response.ProjectResponse; +import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.agent.api.LogLevel; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.network.Network; +import com.cloud.network.Network.IpAddresses; +import com.cloud.offering.DiskOffering; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.VmDetailConstants; +import com.cloud.vm.VmDiskInfo; +import com.cloud.utils.net.Dhcp; + +public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { + + private static final String s_name = "deployvirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") + private Long zoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName}) + private String name; + + @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") + private String displayName; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", + since="4.19.0.0") + protected String password; + + //Owner information + @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") + private String accountName; + + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") + private Long domainId; + + //Network information + //@ACL(accessType = AccessType.UseEntry) + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") + private List networkIds; + + @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootType; + + @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") + private String bootMode; + + @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") + private Boolean bootIntoSetup; + + //DataDisk information + @ACL + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," + + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " + + "offering for the data disk volume. If the templateId parameter passed is from a Template object," + + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " + + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") + private Long size; + + @Parameter(name = ApiConstants.ROOT_DISK_SIZE, + type = CommandType.LONG, + description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", + since = "4.4") + private Long rootdisksize; + + @Parameter(name = ApiConstants.DATADISKS_DETAILS, + type = CommandType.MAP, + since = "4.21.0", + description = "Disk offering details for creating multiple data volumes. Mutually exclusive with diskOfferingId." + + " Example: datadisksdetails[0].diskofferingid=a2a73a84-19db-4852-8930-dfddef053341&datadisksdetails[0].size=10&datadisksdetails[0].miniops=100&datadisksdetails[0].maxiops=200") + private Map dataDisksDetails; + + @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") + private String group; + + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " + + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") + private String hypervisor; + + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, + description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. " + + "This binary data must be base64 encoded before adding it to the request. " + + "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " + + "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + + "You also need to change vm.userdata.max.length value", + length = 1048576) + private String userData; + + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") + private Long userdataId; + + @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") + private Map userdataDetails; + + @Deprecated + @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") + private String sshKeyPairName; + + @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") + private List sshKeyPairNames; + + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") + private Long hostId; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " + + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") + private List securityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." + + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") + private List securityGroupNameList; + + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." + + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") + private Map ipToNetworkList; + + @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") + private String ipAddress; + + @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") + private String ip6Address; + + @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") + private String macAddress; + + @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") + private String keyboard; + + @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") + private Long projectId; + + @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") + private Boolean startVm; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." + + " Mutually exclusive with affinitygroupnames parameter") + private List affinityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." + + "Mutually exclusive with affinitygroupids parameter") + private List affinityGroupNameList; + + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) + private Boolean displayVm; + + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") + private Map details; + + @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) + private String deploymentPlanner; + + @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" + + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") + private Map dhcpOptionsNetworkList; + + @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + + " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") + private Map dataDiskTemplateToDiskOfferingList; + + @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) + private String extraConfig; + + @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") + private Boolean copyImageTags; + + @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", + description = "used to specify the vApp properties.") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppProperties; + + @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", + description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + + " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") + @LogLevel(LogLevel.Log4jLevel.Off) + private Map vAppNetworks; + + @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", + description = "true if virtual machine needs to be dynamically scalable") + protected Boolean dynamicScalingEnabled; + + @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + + "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") + private Long overrideDiskOfferingId; + + @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, + description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") + private Boolean iothreadsEnabled; + + @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") + private String ioDriverPolicy; + + @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", + description = "The number of queues for multiqueue NICs.") + private Integer nicMultiqueueNumber; + + @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", + description = "Enable packed virtqueues or not.") + private Boolean nicPackedVirtQueues; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0", + description = "Number of days instance is leased for.") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + + @Parameter(name = ApiConstants.EXTERNAL_DETAILS, + type = CommandType.MAP, + description = "Details in key/value pairs using format externaldetails[i].keyname=keyvalue. Example: externaldetails[0].server.type=typevalue", + since = "4.21.0") + protected Map externalDetails; + + private List dataDiskInfoList; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + public String getAccountName() { + if (accountName == null) { + return CallContext.current().getCallingAccount().getAccountName(); + } + return accountName; + } + + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public String getDeploymentPlanner() { + return deploymentPlanner; + } + + public String getDisplayName() { + return displayName; + } + + public Long getDomainId() { + if (domainId == null) { + return CallContext.current().getCallingAccount().getDomainId(); + } + return domainId; + } + + public ApiConstants.BootType getBootType() { + if (StringUtils.isNotBlank(bootType)) { + try { + String type = bootType.trim().toUpperCase(); + return ApiConstants.BootType.valueOf(type); + } catch (IllegalArgumentException e) { + String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() + + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + + public Map getDetails() { + Map customparameterMap = convertDetailsToMap(details); + + if (getBootType() != null) { + customparameterMap.put(getBootType().toString(), getBootMode().toString()); + } + + if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { + customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); + } + + IoDriverPolicy ioPolicy = getIoDriverPolicy(); + if (ioPolicy != null) { + customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); + } + + if (BooleanUtils.toBoolean(iothreadsEnabled)) { + customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); + } + + if (nicMultiqueueNumber != null) { + customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); + } + + if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { + customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); + } + + if (MapUtils.isNotEmpty(externalDetails)) { + customparameterMap.putAll(getExternalDetails()); + } + return customparameterMap; + } + + public Map getExternalDetails() { + return convertExternalDetailsToMap(externalDetails); + } + + public ApiConstants.BootMode getBootMode() { + if (StringUtils.isNotBlank(bootMode)) { + try { + String mode = bootMode.trim().toUpperCase(); + return ApiConstants.BootMode.valueOf(mode); + } catch (IllegalArgumentException e) { + String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + } + if (ApiConstants.BootType.UEFI.equals(getBootType())) { + String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", + ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); + logger.error(msg); + throw new InvalidParameterValueException(msg); + } + return null; + } + + public Map getVmProperties() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppProperties)) { + Collection parameterCollection = vAppProperties.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap)iterator.next(); + map.put(entry.get("key"), entry.get("value")); + } + } + return map; + } + + public Map getVmNetworkMap() { + Map map = new HashMap<>(); + if (MapUtils.isNotEmpty(vAppNetworks)) { + Collection parameterCollection = vAppNetworks.values(); + Iterator iterator = parameterCollection.iterator(); + while (iterator.hasNext()) { + HashMap entry = (HashMap) iterator.next(); + Integer nic; + try { + nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); + } catch (NumberFormatException nfe) { + nic = null; + } + String networkUuid = entry.get(VmDetailConstants.NETWORK); + if (logger.isTraceEnabled()) { + logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); + } + if (nic == null || StringUtils.isEmpty(networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { + throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); + } + map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); + } + } + return map; + } + + public String getGroup() { + return group; + } + + public HypervisorType getHypervisor() { + return HypervisorType.getType(hypervisor); + } + + public Boolean isDisplayVm() { + return displayVm; + } + + @Override + public boolean isDisplay() { + if(displayVm == null) + return true; + else + return displayVm; + } + + public List getSecurityGroupNameList() { + return securityGroupNameList; + } + + public List getSecurityGroupIdList() { + return securityGroupIdList; + } + + public Long getSize() { + return size; + } + + public String getUserData() { + return userData; + } + + public Long getUserdataId() { + return userdataId; + } + + public Map getUserdataDetails() { + return convertDetailsToMap(userdataDetails); + } + + public Long getZoneId() { + return zoneId; + } + + public String getPassword() { + return password; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + + public List getNetworkIds() { + if (MapUtils.isNotEmpty(vAppNetworks)) { + if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { + throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); + } else { + return new ArrayList<>(); + } + } + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { + throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); + } else { + List networks = new ArrayList(); + networks.addAll(getIpToNetworkMap().keySet()); + return networks; + } + } + return networkIds; + } + + public String getName() { + return name; + } + + public List getSSHKeyPairNames() { + List sshKeyPairs = new ArrayList(); + if(sshKeyPairNames != null) { + sshKeyPairs = sshKeyPairNames; + } + if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { + sshKeyPairs.add(sshKeyPairName); + } + return sshKeyPairs; + } + + public List getDataDiskInfoList() { + if (this.dataDiskInfoList != null) { + return this.dataDiskInfoList; + } + if (dataDisksDetails == null || dataDisksDetails.isEmpty()) { + return null; + } + if (dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("datadisktemplatetodiskofferinglist parameter can't be specified along with datadisksdetails parameter"); + } + List vmDiskInfoList = new ArrayList<>(); + Collection dataDisksCollection = dataDisksDetails.values(); + Iterator iter = dataDisksCollection.iterator(); + while (iter.hasNext()) { + HashMap dataDisk = (HashMap)iter.next(); + String diskOfferingUuid = dataDisk.get(ApiConstants.DISK_OFFERING_ID); + if (diskOfferingUuid == null) { + throw new InvalidParameterValueException("diskofferingid parameter is required for datadiskdetails"); + } + DiskOffering diskOffering = _entityMgr.findByUuid(DiskOffering.class, diskOfferingUuid); + if (diskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingUuid); + } + if (diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException(String.format("The disk offering id %d provided is directly mapped to a service offering, please provide an individual disk offering", diskOffering.getUuid())); + } + + Long size = null; + Long minIops = null; + Long maxIops = null; + if (dataDisk.get(ApiConstants.DEVICE_ID) == null) { + throw new InvalidParameterValueException("deviceid parameter is required for datadiskdetails"); + } + Long deviceId = Long.parseLong(dataDisk.get(ApiConstants.DEVICE_ID)); + if (diskOffering.isCustomized()) { + if (dataDisk.get(ApiConstants.SIZE) == null) { + throw new InvalidParameterValueException("Size is required for custom disk offering"); + } + size = Long.parseLong(dataDisk.get(ApiConstants.SIZE)); + } else { + size = diskOffering.getDiskSize() / (1024 * 1024 * 1024); + } + if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { + if (dataDisk.get(ApiConstants.MIN_IOPS) == null) { + throw new InvalidParameterValueException("Min IOPS is required for custom disk offering"); + } + if (dataDisk.get(ApiConstants.MAX_IOPS) == null) { + throw new InvalidParameterValueException("Max IOPS is required for custom disk offering"); + } + minIops = Long.parseLong(dataDisk.get(ApiConstants.MIN_IOPS)); + maxIops = Long.parseLong(dataDisk.get(ApiConstants.MAX_IOPS)); + } + VmDiskInfo vmDiskInfo = new VmDiskInfo(diskOffering, size, minIops, maxIops, deviceId); + vmDiskInfoList.add(vmDiskInfo); + } + this.dataDiskInfoList = vmDiskInfoList; + return dataDiskInfoList; + } + + public Long getHostId() { + return hostId; + } + + public boolean getStartVm() { + return startVm == null ? true : startVm; + } + + public Map getIpToNetworkMap() { + if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { + throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); + } + LinkedHashMap ipToNetworkMap = null; + if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { + ipToNetworkMap = new LinkedHashMap(); + Collection ipsCollection = ipToNetworkList.values(); + Iterator iter = ipsCollection.iterator(); + while (iter.hasNext()) { + HashMap ips = (HashMap)iter.next(); + Long networkId = getNetworkIdFomIpMap(ips); + IpAddresses addrs = getIpAddressesFromIpMap(ips); + ipToNetworkMap.put(networkId, addrs); + } + } + + return ipToNetworkMap; + } + + @Nonnull + private IpAddresses getIpAddressesFromIpMap(HashMap ips) { + String requestedIp = ips.get("ip"); + String requestedIpv6 = ips.get("ipv6"); + String requestedMac = ips.get("mac"); + if (requestedIpv6 != null) { + requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); + } + if (requestedMac != null) { + if(!NetUtils.isValidMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); + } else if(!NetUtils.isUnicastMac(requestedMac)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); + } + requestedMac = NetUtils.standardizeMacAddress(requestedMac); + } + return new IpAddresses(requestedIp, requestedIpv6, requestedMac); + } + + @Nonnull + private Long getNetworkIdFomIpMap(HashMap ips) { + Long networkId; + final String networkid = ips.get("networkid"); + Network network = _networkService.getNetwork(networkid); + if (network != null) { + networkId = network.getId(); + } else { + try { + networkId = Long.parseLong(networkid); + } catch (NumberFormatException e) { + throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); + } + } + return networkId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getIp6Address() { + if (ip6Address == null) { + return null; + } + return NetUtils.standardizeIp6Address(ip6Address); + } + + + public String getMacAddress() { + if (macAddress == null) { + return null; + } + if(!NetUtils.isValidMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); + } else if(!NetUtils.isUnicastMac(macAddress)) { + throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); + } + return NetUtils.standardizeMacAddress(macAddress); + } + + public List getAffinityGroupIdList() { + if (affinityGroupNameList != null && affinityGroupIdList != null) { + throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); + } + + // transform group names to ids here + if (affinityGroupNameList != null) { + List affinityGroupIds = new ArrayList(); + for (String groupName : affinityGroupNameList) { + Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); + if (groupId == null) { + throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); + } else { + affinityGroupIds.add(groupId); + } + } + return affinityGroupIds; + } else { + return affinityGroupIdList; + } + } + + public String getKeyboard() { + // TODO Auto-generated method stub + return keyboard; + } + + public Map> getDhcpOptionsMap() { + Map> dhcpOptionsMap = new HashMap<>(); + if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { + + Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); + for (Map dhcpNetworkOptions : paramsCollection) { + String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); + + if (networkId == null) { + throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); + } + + Map dhcpOptionsForNetwork = new HashMap<>(); + dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); + + for (String key : dhcpNetworkOptions.keySet()) { + if (key.startsWith(ApiConstants.DHCP_PREFIX)) { + int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); + dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); + } else if (!key.equals(ApiConstants.NETWORK_ID)) { + Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); + dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); + } + } + + } + } + + return dhcpOptionsMap; + } + + public Map getDataDiskTemplateToDiskOfferingMap() { + if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { + throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); + } + if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { + return new HashMap(); + } + + HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); + for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { + HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; + Long dataDiskTemplateId; + DiskOffering dataDiskOffering = null; + VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) { + dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); + if (dataDiskTemplate == null) + throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); + } + dataDiskTemplateId = dataDiskTemplate.getId(); + dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) { + dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); + if (dataDiskOffering == null) + throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); + } + dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); + } + return dataDiskTemplateToDiskOfferingMap; + } + + public String getExtraConfig() { + return extraConfig; + } + + public boolean getCopyImageTags() { + return copyImageTags == null ? false : copyImageTags; + } + + public Boolean getBootIntoSetup() { + return bootIntoSetup; + } + + public boolean isDynamicScalingEnabled() { + return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; + } + + public Long getOverrideDiskOfferingId() { + return overrideDiskOfferingId; + } + + public IoDriverPolicy getIoDriverPolicy() { + if (StringUtils.isNotBlank(ioDriverPolicy)) { + try { + String policyType = ioDriverPolicy.trim().toUpperCase(); + return IoDriverPolicy.valueOf(policyType); + } catch (IllegalArgumentException e) { + String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(IoDriverPolicy.values())); + logger.warn(errMesg); + throw new InvalidParameterValueException(errMesg); + } + } + return null; + } + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public long getEntityOwnerId() { + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); + if (accountId == null) { + return CallContext.current().getCallingAccount().getId(); + } + + return accountId; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_VM_CREATE; + } + + @Override + public String getCreateEventDescription() { + return "creating Vm"; + } + + @Override + public String getEventDescription() { + if(getStartVm()) { + return "starting Vm. Vm Id: " + getEntityUuid(); + } + return "deploying Vm. Vm Id: " + getEntityUuid(); + } + + @Override + public ApiCommandResourceType getApiResourceType() { + return ApiCommandResourceType.VirtualMachine; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java new file mode 100644 index 00000000000..04e413ed67a --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/CreateVMFromBackupCmd.java @@ -0,0 +1,153 @@ +// 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.user.vm; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.BackupResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserVmResponse; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; + +@APICommand(name = "createVMFromBackup", + description = "Creates and automatically starts a VM from a backup.", + responseObject = UserVmResponse.class, + responseView = ResponseObject.ResponseView.Restricted, + entityType = {VirtualMachine.class}, + requestHasSensitiveInfo = false, responseHasSensitiveInfo = true, + since = "4.21.0", + authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}) +public class CreateVMFromBackupCmd extends BaseDeployVMCmd { + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.BACKUP_ID, + type = CommandType.UUID, + entityType = BackupResponse.class, + required = true, + description = "backup ID to create the VM from") + private Long backupId; + + @ACL + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the ID of the service offering for the virtual machine") + private Long serviceOfferingId; + + @ACL + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine") + private Long templateId; + + @Parameter(name = ApiConstants.PRESERVE_IP, type = CommandType.BOOLEAN, description = "Use the same IP/MAC addresses as stored in the backup metadata. Works only if the original Instance is deleted and the IP/MAC address is available.") + private Boolean preserveIp; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getBackupId() { + return backupId; + } + + public Long getServiceOfferingId() { + return serviceOfferingId; + } + + public Long getTemplateId() { + return templateId; + } + + public boolean getPreserveIp() { + return (preserveIp != null) ? preserveIp : false; + } + + @Override + public void create() { + UserVm vm; + try { + vm = _userVmService.allocateVMFromBackup(this); + if (vm != null) { + setEntityId(vm.getId()); + setEntityUuid(vm.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm"); + } + } catch (InsufficientCapacityException ex) { + logger.info(ex); + logger.trace(ex.getMessage(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, ex.getMessage()); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } + } + + @Override + public void execute () { + UserVm vm = null; + try { + vm = _userVmService.restoreVMFromBackup(this); + } catch (ResourceUnavailableException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage()); + } catch (ResourceAllocationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); + } catch (ConcurrentOperationException ex) { + logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } catch (InsufficientCapacityException ex) { + StringBuilder message = new StringBuilder(ex.getMessage()); + if (ex instanceof InsufficientServerCapacityException) { + if (((InsufficientServerCapacityException)ex).isAffinityApplied()) { + message.append(", Please check the affinity groups provided, there may not be sufficient capacity to follow them"); + } + } + logger.info(String.format("%s: %s", message.toString(), ex.getLocalizedMessage())); + logger.debug(message.toString(), ex); + throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, message.toString()); + } + + if (vm != null) { + UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", vm).get(0); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to deploy vm uuid:"+getEntityUuid()); + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 52d42a95d98..dc1ca3583cc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -16,761 +16,77 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; -import javax.annotation.Nonnull; - -import org.apache.cloudstack.acl.RoleType; -import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiArgValidator; -import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCreateCustomIdCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.user.UserCmd; -import org.apache.cloudstack.api.response.DiskOfferingResponse; -import org.apache.cloudstack.api.response.DomainResponse; -import org.apache.cloudstack.api.response.HostResponse; -import org.apache.cloudstack.api.response.NetworkResponse; -import org.apache.cloudstack.api.response.ProjectResponse; -import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.SnapshotResponse; import org.apache.cloudstack.api.response.TemplateResponse; -import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import com.cloud.agent.api.LogLevel; -import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.network.Network; -import com.cloud.network.Network.IpAddresses; -import com.cloud.offering.DiskOffering; -import com.cloud.template.VirtualMachineTemplate; import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.utils.net.NetUtils; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VmDetailConstants; @APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction, UserCmd { - - private static final String s_name = "deployvirtualmachineresponse"; +public class DeployVMCmd extends BaseDeployVMCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") - private Long zoneId; - @ACL @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the ID of the service offering for the virtual machine") private Long serviceOfferingId; @ACL - @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template for the virtual machine") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine") private Long templateId; - @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName}) - private String name; + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, since = "4.21") + private Long volumeId; - @Parameter(name = ApiConstants.DISPLAY_NAME, type = CommandType.STRING, description = "an optional user generated name for the virtual machine") - private String displayName; - - @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="The password of the virtual machine. If null, a random password will be generated for the VM.", - since="4.19.0.0") - protected String password; - - //Owner information - @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") - private String accountName; - - @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used. If account is NOT provided then virtual machine will be assigned to the caller account and domain.") - private Long domainId; - - //Network information - //@ACL(accessType = AccessType.UseEntry) - @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") - private List networkIds; - - @Parameter(name = ApiConstants.BOOT_TYPE, type = CommandType.STRING, required = false, description = "Guest VM Boot option either custom[UEFI] or default boot [BIOS]. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootType; - - @Parameter(name = ApiConstants.BOOT_MODE, type = CommandType.STRING, required = false, description = "Boot Mode [Legacy] or [Secure] Applicable when Boot Type Selected is UEFI, otherwise Legacy only for BIOS. Not applicable with VMware if the template is marked as deploy-as-is, as we honour what is defined in the template.", since = "4.14.0.0") - private String bootMode; - - @Parameter(name = ApiConstants.BOOT_INTO_SETUP, type = CommandType.BOOLEAN, required = false, description = "Boot into hardware setup or not (ignored if startVm = false, only valid for vmware)", since = "4.15.0.0") - private Boolean bootIntoSetup; - - //DataDisk information - @ACL - @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," - + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " - + "offering for the data disk volume. If the templateId parameter passed is from a Template object," - + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " - + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") - private Long diskOfferingId; - - @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") - private Long size; - - @Parameter(name = ApiConstants.ROOT_DISK_SIZE, - type = CommandType.LONG, - description = "Optional field to resize root disk on deploy. Value is in GB. Only applies to template-based deployments. Analogous to details[0].rootdisksize, which takes precedence over this parameter if both are provided", - since = "4.4") - private Long rootdisksize; - - @Parameter(name = ApiConstants.GROUP, type = CommandType.STRING, description = "an optional group for the virtual machine") - private String group; - - @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine. " - + "The parameter is required and respected only when hypervisor info is not set on the ISO/Template passed to the call") - private String hypervisor; - - @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, - description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. " + - "This binary data must be base64 encoded before adding it to the request. " + - "Using HTTP GET (via querystring), you can send up to 4KB of data after base64 encoding. " + - "Using HTTP POST (via POST body), you can send up to 1MB of data after base64 encoding. " + - "You also need to change vm.userdata.max.length value", - length = 1048576) - private String userData; - - @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18") - private Long userdataId; - - @Parameter(name = ApiConstants.USER_DATA_DETAILS, type = CommandType.MAP, description = "used to specify the parameters values for the variables in userdata.", since = "4.18") - private Map userdataDetails; - - @Deprecated - @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") - private String sshKeyPairName; - - @Parameter(name = ApiConstants.SSH_KEYPAIRS, type = CommandType.LIST, collectionType = CommandType.STRING, since="4.17", description = "names of the ssh key pairs used to login to the virtual machine") - private List sshKeyPairNames; - - @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") - private Long hostId; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " - + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") - private List securityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." - + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") - private List securityGroupNameList; - - @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." - + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid&iptonetworklist[0].mac=aa:bb:cc:dd:ee::ff - requests to use ip 10.10.10.11 in network id=uuid") - private Map ipToNetworkList; - - @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") - private String ipAddress; - - @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") - private String ip6Address; - - @Parameter(name = ApiConstants.MAC_ADDRESS, type = CommandType.STRING, description = "the mac address for default vm's network") - private String macAddress; - - @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") - private String keyboard; - - @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") - private Long projectId; - - @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if start vm after creating; defaulted to true if not specified") - private Boolean startVm; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." - + " Mutually exclusive with affinitygroupnames parameter") - private List affinityGroupIdList; - - @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." - + "Mutually exclusive with affinitygroupids parameter") - private List affinityGroupNameList; - - @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) - private Boolean displayVm; - - @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters. 'extraconfig' is not allowed to be passed in details") - private Map details; - - @Parameter(name = ApiConstants.DEPLOYMENT_PLANNER, type = CommandType.STRING, description = "Deployment planner to use for vm allocation. Available to ROOT admin only", since = "4.4", authorized = { RoleType.Admin }) - private String deploymentPlanner; - - @Parameter(name = ApiConstants.DHCP_OPTIONS_NETWORK_LIST, type = CommandType.MAP, description = "DHCP options which are passed to the VM on start up" - + " Example: dhcpoptionsnetworklist[0].dhcp:114=url&dhcpoptionsetworklist[0].networkid=networkid&dhcpoptionsetworklist[0].dhcp:66=www.test.com") - private Map dhcpOptionsNetworkList; - - @Parameter(name = ApiConstants.DATADISK_OFFERING_LIST, type = CommandType.MAP, since = "4.11", description = "datadisk template to disk-offering mapping;" + - " an optional parameter used to create additional data disks from datadisk templates; can't be specified with diskOfferingId parameter") - private Map dataDiskTemplateToDiskOfferingList; - - @Parameter(name = ApiConstants.EXTRA_CONFIG, type = CommandType.STRING, since = "4.12", description = "an optional URL encoded string that can be passed to the virtual machine upon successful deployment", length = 5120) - private String extraConfig; - - @Parameter(name = ApiConstants.COPY_IMAGE_TAGS, type = CommandType.BOOLEAN, since = "4.13", description = "if true the image tags (if any) will be copied to the VM, default value is false") - private Boolean copyImageTags; - - @Parameter(name = ApiConstants.PROPERTIES, type = CommandType.MAP, since = "4.15", - description = "used to specify the vApp properties.") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppProperties; - - @Parameter(name = ApiConstants.NIC_NETWORK_LIST, type = CommandType.MAP, since = "4.15", - description = "VMware only: used to specify network mapping of a vApp VMware template registered \"as-is\"." + - " Example nicnetworklist[0].ip=Nic-101&nicnetworklist[0].network=uuid") - @LogLevel(LogLevel.Log4jLevel.Off) - private Map vAppNetworks; - - @Parameter(name = ApiConstants.DYNAMIC_SCALING_ENABLED, type = CommandType.BOOLEAN, since = "4.16", - description = "true if virtual machine needs to be dynamically scalable") - protected Boolean dynamicScalingEnabled; - - @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + - "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") - private Long overrideDiskOfferingId; - - @Parameter(name = ApiConstants.IOTHREADS_ENABLED, type = CommandType.BOOLEAN, required = false, - description = "IOThreads are dedicated event loop threads for supported disk devices to perform block I/O requests in order to improve scalability especially on an SMP host/guest with many LUNs.") - private Boolean iothreadsEnabled; - - @Parameter(name = ApiConstants.IO_DRIVER_POLICY, type = CommandType.STRING, description = "Controls specific policies on IO") - private String ioDriverPolicy; - - @Parameter(name = ApiConstants.NIC_MULTIQUEUE_NUMBER, type = CommandType.INTEGER, since = "4.18", - description = "The number of queues for multiqueue NICs.") - private Integer nicMultiqueueNumber; - - @Parameter(name = ApiConstants.NIC_PACKED_VIRTQUEUES_ENABLED, type = CommandType.BOOLEAN, since = "4.18", - description = "Enable packed virtqueues or not.") - private Boolean nicPackedVirtQueues; + @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21") + private Long snapshotId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public String getAccountName() { - if (accountName == null) { - return CallContext.current().getCallingAccount().getAccountName(); - } - return accountName; - } - - public Long getDiskOfferingId() { - return diskOfferingId; - } - - public String getDeploymentPlanner() { - return deploymentPlanner; - } - - public String getDisplayName() { - return displayName; - } - - public Long getDomainId() { - if (domainId == null) { - return CallContext.current().getCallingAccount().getDomainId(); - } - return domainId; - } - - public ApiConstants.BootType getBootType() { - if (StringUtils.isNotBlank(bootType)) { - try { - String type = bootType.trim().toUpperCase(); - return ApiConstants.BootType.valueOf(type); - } catch (IllegalArgumentException e) { - String errMesg = "Invalid bootType " + bootType + "Specified for vm " + getName() - + " Valid values are: " + Arrays.toString(ApiConstants.BootType.values()); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - - public Map getDetails() { - Map customparameterMap = convertDetailsToMap(details); - - if (getBootType() != null) { - customparameterMap.put(getBootType().toString(), getBootMode().toString()); - } - - if (rootdisksize != null && !customparameterMap.containsKey(VmDetailConstants.ROOT_DISK_SIZE)) { - customparameterMap.put(VmDetailConstants.ROOT_DISK_SIZE, rootdisksize.toString()); - } - - IoDriverPolicy ioPolicy = getIoDriverPolicy(); - if (ioPolicy != null) { - customparameterMap.put(VmDetailConstants.IO_POLICY, ioPolicy.toString()); - } - - if (BooleanUtils.toBoolean(iothreadsEnabled)) { - customparameterMap.put(VmDetailConstants.IOTHREADS, BooleanUtils.toStringTrueFalse(iothreadsEnabled)); - } - - if (nicMultiqueueNumber != null) { - customparameterMap.put(VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber.toString()); - } - - if (BooleanUtils.toBoolean(nicPackedVirtQueues)) { - customparameterMap.put(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, BooleanUtils.toStringTrueFalse(nicPackedVirtQueues)); - } - - return customparameterMap; - } - - - public ApiConstants.BootMode getBootMode() { - if (StringUtils.isNotBlank(bootMode)) { - try { - String mode = bootMode.trim().toUpperCase(); - return ApiConstants.BootMode.valueOf(mode); - } catch (IllegalArgumentException e) { - String msg = String.format("Invalid %s: %s specified for VM: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, bootMode, getName(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - } - if (ApiConstants.BootType.UEFI.equals(getBootType())) { - String msg = String.format("%s must be specified for the VM with boot type: %s. Valid values are: %s", - ApiConstants.BOOT_MODE, getBootType(), Arrays.toString(ApiConstants.BootMode.values())); - logger.error(msg); - throw new InvalidParameterValueException(msg); - } - return null; - } - - public Map getVmProperties() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppProperties)) { - Collection parameterCollection = vAppProperties.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap)iterator.next(); - map.put(entry.get("key"), entry.get("value")); - } - } - return map; - } - - public Map getVmNetworkMap() { - Map map = new HashMap<>(); - if (MapUtils.isNotEmpty(vAppNetworks)) { - Collection parameterCollection = vAppNetworks.values(); - Iterator iterator = parameterCollection.iterator(); - while (iterator.hasNext()) { - HashMap entry = (HashMap) iterator.next(); - Integer nic; - try { - nic = Integer.valueOf(entry.get(VmDetailConstants.NIC)); - } catch (NumberFormatException nfe) { - nic = null; - } - String networkUuid = entry.get(VmDetailConstants.NETWORK); - if (logger.isTraceEnabled()) { - logger.trace(String.format("nic, '%s', goes on net, '%s'", nic, networkUuid)); - } - if (nic == null || StringUtils.isEmpty(networkUuid) || _entityMgr.findByUuid(Network.class, networkUuid) == null) { - throw new InvalidParameterValueException(String.format("Network ID: %s for NIC ID: %s is invalid", networkUuid, nic)); - } - map.put(nic, _entityMgr.findByUuid(Network.class, networkUuid).getId()); - } - } - return map; - } - - public String getGroup() { - return group; - } - - public HypervisorType getHypervisor() { - return HypervisorType.getType(hypervisor); - } - - public Boolean isDisplayVm() { - return displayVm; - } - - @Override - public boolean isDisplay() { - if(displayVm == null) - return true; - else - return displayVm; - } - - public List getSecurityGroupNameList() { - return securityGroupNameList; - } - - public List getSecurityGroupIdList() { - return securityGroupIdList; - } - public Long getServiceOfferingId() { return serviceOfferingId; } - public Long getSize() { - return size; - } - public Long getTemplateId() { return templateId; } - public String getUserData() { - return userData; + public Long getVolumeId() { + return volumeId; } - public Long getUserdataId() { - return userdataId; + public Long getSnapshotId() { + return snapshotId; } - public Map getUserdataDetails() { - return convertDetailsToMap(userdataDetails); + public boolean isVolumeOrSnapshotProvided() { + return volumeId != null || snapshotId != null; } - - public Long getZoneId() { - return zoneId; - } - - public String getPassword() { - return password; - } - - public List getNetworkIds() { - if (MapUtils.isNotEmpty(vAppNetworks)) { - if (CollectionUtils.isNotEmpty(networkIds) || ipAddress != null || getIp6Address() != null || MapUtils.isNotEmpty(ipToNetworkList)) { - throw new InvalidParameterValueException(String.format("%s can't be specified along with %s, %s, %s", ApiConstants.NIC_NETWORK_LIST, ApiConstants.NETWORK_IDS, ApiConstants.IP_ADDRESS, ApiConstants.IP_NETWORK_LIST)); - } else { - return new ArrayList<>(); - } - } - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - if ((networkIds != null && !networkIds.isEmpty()) || ipAddress != null || getIp6Address() != null) { - throw new InvalidParameterValueException("ipToNetworkMap can't be specified along with networkIds or ipAddress"); - } else { - List networks = new ArrayList(); - networks.addAll(getIpToNetworkMap().keySet()); - return networks; - } - } - return networkIds; - } - - public String getName() { - return name; - } - - public List getSSHKeyPairNames() { - List sshKeyPairs = new ArrayList(); - if(sshKeyPairNames != null) { - sshKeyPairs = sshKeyPairNames; - } - if(sshKeyPairName != null && !sshKeyPairName.isEmpty()) { - sshKeyPairs.add(sshKeyPairName); - } - return sshKeyPairs; - } - - public Long getHostId() { - return hostId; - } - - public boolean getStartVm() { - return startVm == null ? true : startVm; - } - - public Map getIpToNetworkMap() { - if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) { - throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter"); - } - LinkedHashMap ipToNetworkMap = null; - if (ipToNetworkList != null && !ipToNetworkList.isEmpty()) { - ipToNetworkMap = new LinkedHashMap(); - Collection ipsCollection = ipToNetworkList.values(); - Iterator iter = ipsCollection.iterator(); - while (iter.hasNext()) { - HashMap ips = (HashMap)iter.next(); - Long networkId = getNetworkIdFomIpMap(ips); - IpAddresses addrs = getIpAddressesFromIpMap(ips); - ipToNetworkMap.put(networkId, addrs); - } - } - - return ipToNetworkMap; - } - - @Nonnull - private IpAddresses getIpAddressesFromIpMap(HashMap ips) { - String requestedIp = ips.get("ip"); - String requestedIpv6 = ips.get("ipv6"); - String requestedMac = ips.get("mac"); - if (requestedIpv6 != null) { - requestedIpv6 = NetUtils.standardizeIp6Address(requestedIpv6); - } - if (requestedMac != null) { - if(!NetUtils.isValidMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not valid: " + requestedMac); - } else if(!NetUtils.isUnicastMac(requestedMac)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + requestedMac); - } - requestedMac = NetUtils.standardizeMacAddress(requestedMac); - } - return new IpAddresses(requestedIp, requestedIpv6, requestedMac); - } - - @Nonnull - private Long getNetworkIdFomIpMap(HashMap ips) { - Long networkId; - final String networkid = ips.get("networkid"); - Network network = _networkService.getNetwork(networkid); - if (network != null) { - networkId = network.getId(); - } else { - try { - networkId = Long.parseLong(networkid); - } catch (NumberFormatException e) { - throw new InvalidParameterValueException("Unable to translate and find entity with networkId: " + networkid); - } - } - return networkId; - } - - public String getIpAddress() { - return ipAddress; - } - - public String getIp6Address() { - if (ip6Address == null) { - return null; - } - return NetUtils.standardizeIp6Address(ip6Address); - } - - - public String getMacAddress() { - if (macAddress == null) { - return null; - } - if(!NetUtils.isValidMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not valid: " + macAddress); - } else if(!NetUtils.isUnicastMac(macAddress)) { - throw new InvalidParameterValueException("Mac address is not unicast: " + macAddress); - } - return NetUtils.standardizeMacAddress(macAddress); - } - - public List getAffinityGroupIdList() { - if (affinityGroupNameList != null && affinityGroupIdList != null) { - throw new InvalidParameterValueException("affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); - } - - // transform group names to ids here - if (affinityGroupNameList != null) { - List affinityGroupIds = new ArrayList(); - for (String groupName : affinityGroupNameList) { - Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); - if (groupId == null) { - throw new InvalidParameterValueException("Unable to find affinity group by name " + groupName); - } else { - affinityGroupIds.add(groupId); - } - } - return affinityGroupIds; - } else { - return affinityGroupIdList; - } - } - - public String getKeyboard() { - // TODO Auto-generated method stub - return keyboard; - } - - public Map> getDhcpOptionsMap() { - Map> dhcpOptionsMap = new HashMap<>(); - if (dhcpOptionsNetworkList != null && !dhcpOptionsNetworkList.isEmpty()) { - - Collection> paramsCollection = this.dhcpOptionsNetworkList.values(); - for (Map dhcpNetworkOptions : paramsCollection) { - String networkId = dhcpNetworkOptions.get(ApiConstants.NETWORK_ID); - - if (networkId == null) { - throw new IllegalArgumentException("No networkid specified when providing extra dhcp options."); - } - - Map dhcpOptionsForNetwork = new HashMap<>(); - dhcpOptionsMap.put(networkId, dhcpOptionsForNetwork); - - for (String key : dhcpNetworkOptions.keySet()) { - if (key.startsWith(ApiConstants.DHCP_PREFIX)) { - int dhcpOptionValue = Integer.parseInt(key.replaceFirst(ApiConstants.DHCP_PREFIX, "")); - dhcpOptionsForNetwork.put(dhcpOptionValue, dhcpNetworkOptions.get(key)); - } else if (!key.equals(ApiConstants.NETWORK_ID)) { - Dhcp.DhcpOptionCode dhcpOptionEnum = Dhcp.DhcpOptionCode.valueOfString(key); - dhcpOptionsForNetwork.put(dhcpOptionEnum.getCode(), dhcpNetworkOptions.get(key)); - } - } - - } - } - - return dhcpOptionsMap; - } - - public Map getDataDiskTemplateToDiskOfferingMap() { - if (diskOfferingId != null && dataDiskTemplateToDiskOfferingList != null) { - throw new InvalidParameterValueException("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter"); - } - if (MapUtils.isEmpty(dataDiskTemplateToDiskOfferingList)) { - return new HashMap(); - } - - HashMap dataDiskTemplateToDiskOfferingMap = new HashMap(); - for (Object objDataDiskTemplates : dataDiskTemplateToDiskOfferingList.values()) { - HashMap dataDiskTemplates = (HashMap) objDataDiskTemplates; - Long dataDiskTemplateId; - DiskOffering dataDiskOffering = null; - VirtualMachineTemplate dataDiskTemplate= _entityMgr.findByUuid(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) { - dataDiskTemplate = _entityMgr.findById(VirtualMachineTemplate.class, dataDiskTemplates.get("datadisktemplateid")); - if (dataDiskTemplate == null) - throw new InvalidParameterValueException("Unable to translate and find entity with datadisktemplateid " + dataDiskTemplates.get("datadisktemplateid")); - } - dataDiskTemplateId = dataDiskTemplate.getId(); - dataDiskOffering = _entityMgr.findByUuid(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) { - dataDiskOffering = _entityMgr.findById(DiskOffering.class, dataDiskTemplates.get("diskofferingid")); - if (dataDiskOffering == null) - throw new InvalidParameterValueException("Unable to translate and find entity with diskofferingId " + dataDiskTemplates.get("diskofferingid")); - } - dataDiskTemplateToDiskOfferingMap.put(dataDiskTemplateId, dataDiskOffering); - } - return dataDiskTemplateToDiskOfferingMap; - } - - public String getExtraConfig() { - return extraConfig; - } - - public boolean getCopyImageTags() { - return copyImageTags == null ? false : copyImageTags; - } - - public Boolean getBootIntoSetup() { - return bootIntoSetup; - } - - public boolean isDynamicScalingEnabled() { - return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; - } - - public Long getOverrideDiskOfferingId() { - return overrideDiskOfferingId; - } - - public ApiConstants.IoDriverPolicy getIoDriverPolicy() { - if (StringUtils.isNotBlank(ioDriverPolicy)) { - try { - String policyType = ioDriverPolicy.trim().toUpperCase(); - return ApiConstants.IoDriverPolicy.valueOf(policyType); - } catch (IllegalArgumentException e) { - String errMesg = String.format("Invalid io policy %s specified for vm %s. Valid values are: %s", ioDriverPolicy, getName(), Arrays.toString(ApiConstants.IoDriverPolicy.values())); - logger.warn(errMesg); - throw new InvalidParameterValueException(errMesg); - } - } - return null; - } - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - public static String getResultObjectName() { - return "virtualmachine"; - } - - @Override - public long getEntityOwnerId() { - Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); - if (accountId == null) { - return CallContext.current().getCallingAccount().getId(); - } - - return accountId; - } - - @Override - public String getEventType() { - return EventTypes.EVENT_VM_CREATE; - } - - @Override - public String getCreateEventType() { - return EventTypes.EVENT_VM_CREATE; - } - - @Override - public String getCreateEventDescription() { - return "creating Vm"; - } - - @Override - public String getEventDescription() { - if(getStartVm()) { - return "starting Vm. Vm Id: " + getEntityUuid(); - } - return "deploying Vm. Vm Id: " + getEntityUuid(); - } - - @Override - public ApiCommandResourceType getApiResourceType() { - return ApiCommandResourceType.VirtualMachine; - } - @Override public void execute() { UserVm result; @@ -813,9 +129,12 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG } } - @Override public void create() throws ResourceAllocationException { + if (Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) { + throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID"); + } + try { UserVm vm = _userVmService.createVirtualMachine(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java index aa121162cb4..18a9d2058a6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java @@ -140,7 +140,8 @@ public class DestroyVMCmd extends BaseAsyncCmd implements UserCmd { if (responses != null && !responses.isEmpty()) { response = responses.get(0); } - response.setResponseName("virtualmachine"); + response.setResponseName(getCommandName()); + response.setObjectName("virtualmachine"); setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to destroy vm"); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index 50e1798112d..a561b6fd24f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -16,10 +16,15 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -33,6 +38,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; import org.apache.cloudstack.api.response.BackupOfferingResponse; +import org.apache.cloudstack.api.response.ExtensionResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.IsoVmResponse; import org.apache.cloudstack.api.response.ListResponse; @@ -41,16 +47,20 @@ import org.apache.cloudstack.api.response.ResourceIconResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import com.cloud.cpu.CPU; import com.cloud.exception.InvalidParameterValueException; import com.cloud.server.ResourceIcon; import com.cloud.server.ResourceTag; +import com.cloud.storage.GuestOS; import com.cloud.vm.VirtualMachine; @@ -149,6 +159,30 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements @Parameter(name = ApiConstants.USER_DATA, type = CommandType.BOOLEAN, description = "Whether to return the VMs' user data or not. By default, user data will not be returned.", since = "4.18.0.0") private Boolean showUserData; + @Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, required = false, description = "the instances by userdata", since = "4.20.1") + private Long userdataId; + + @Parameter(name = ApiConstants.ARCH, type = CommandType.STRING, + description = "CPU arch of the VM", + since = "4.20.1") + private String arch; + + @Parameter(name = ApiConstants.LEASED, type = CommandType.BOOLEAN, + description = "Whether to return only leased instances", + since = "4.21.0") + private Boolean onlyLeasedInstances = false; + + @Parameter(name = ApiConstants.GPU_ENABLED, + type = CommandType.BOOLEAN, + description = "Flag to indicate if the VMs should be filtered by GPU support. If set to true, only VMs that support GPU will be returned.", + since = "4.21.0") + private Boolean gpuEnabled; + + @Parameter(name = ApiConstants.EXTENSION_ID, type = CommandType.UUID, + entityType = ExtensionResponse.class, description = "The ID of the Orchestrator extension for the VM", + since = "4.21.0") + private Long extensionId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -243,6 +277,10 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements return CollectionUtils.isEmpty(viewDetails); } + public Long getUserdataId() { + return userdataId; + } + public EnumSet getDetails() throws InvalidParameterValueException { if (isViewDetailsEmpty()) { if (_queryService.ReturnVmStatsOnVmList.value()) { @@ -284,6 +322,22 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements return isVnf; } + public CPU.CPUArch getArch() { + return StringUtils.isBlank(arch) ? null : CPU.CPUArch.fromType(arch); + } + + public boolean getOnlyLeasedInstances() { + return BooleanUtils.toBoolean(onlyLeasedInstances); + } + + public Boolean getGpuEnabled() { + return gpuEnabled; + } + + public Long getExtensionId() { + return extensionId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -303,22 +357,75 @@ public class ListVMsCmd extends BaseListRetrieveOnlyResourceCountCmd implements setResponseObject(response); } - protected void updateVMResponse(List response) { - for (UserVmResponse vmResponse : response) { - ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.UserVm, vmResponse.getId()); - if (resourceIcon == null) { - ResourceTag.ResourceObjectType type = ResourceTag.ResourceObjectType.Template; - String uuid = vmResponse.getTemplateId(); - if (vmResponse.getIsoId() != null) { - uuid = vmResponse.getIsoId(); - type = ResourceTag.ResourceObjectType.ISO; - } - resourceIcon = resourceIconManager.getByResourceTypeAndUuid(type, uuid); - if (resourceIcon == null) { - continue; - } + protected Map getResourceIconsUsingOsCategory(List responses) { + Set guestOsIds = responses.stream().map(UserVmResponse::getGuestOsId).collect(Collectors.toSet()); + List guestOSList = _entityMgr.listByUuids(GuestOS.class, guestOsIds); + Map guestOSMap = guestOSList.stream() + .collect(Collectors.toMap(GuestOS::getUuid, Function.identity())); + Set guestOsCategoryIds = guestOSMap.values().stream() + .map(GuestOS::getCategoryId) + .collect(Collectors.toSet()); + Map guestOsCategoryIcons = + resourceIconManager.getByResourceTypeAndIds(ResourceTag.ResourceObjectType.GuestOsCategory, + guestOsCategoryIds); + Map vmIcons = new HashMap<>(); + for (UserVmResponse response : responses) { + GuestOS guestOS = guestOSMap.get(response.getGuestOsId()); + if (guestOS != null) { + vmIcons.put(response.getId(), guestOsCategoryIcons.get(guestOS.getCategoryId())); } - ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(resourceIcon); + } + return vmIcons; + } + + protected Map getResourceIconsForUsingTemplateIso(List responses) { + Map vmTemplateIsoIdMap = new HashMap<>(); + Set templateUuids = new HashSet<>(); + Set isoUuids = new HashSet<>(); + for (UserVmResponse vmResponse : responses) { + if (vmResponse.getTemplateId() != null) { + templateUuids.add(vmResponse.getTemplateId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getTemplateId()); + } + if (vmResponse.getIsoId() != null) { + isoUuids.add(vmResponse.getIsoId()); + vmTemplateIsoIdMap.put(vmResponse.getId(), vmResponse.getIsoId()); + } + } + Map templateOrIsoIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, templateUuids); + templateOrIsoIcons.putAll(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, isoUuids)); + Map vmIcons = new HashMap<>(); + List noTemplateIsoIconResponses = new ArrayList<>(); + for (UserVmResponse response : responses) { + String uuid = vmTemplateIsoIdMap.get(response.getId()); + if (StringUtils.isNotBlank(uuid) && templateOrIsoIcons.containsKey(uuid)) { + vmIcons.put(response.getId(), + templateOrIsoIcons.get(vmTemplateIsoIdMap.get(response.getId()))); + continue; + } + noTemplateIsoIconResponses.add(response); + } + vmIcons.putAll(getResourceIconsUsingOsCategory(noTemplateIsoIconResponses)); + return vmIcons; + } + + protected void updateVMResponse(List responses) { + if (CollectionUtils.isEmpty(responses)) { + return; + } + Set vmUuids = responses.stream().map(UserVmResponse::getId).collect(Collectors.toSet()); + Map vmIcons = resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, vmUuids); + List noVmIconResponses = responses + .stream() + .filter(r -> !vmIcons.containsKey(r.getId())) + .collect(Collectors.toList()); + vmIcons.putAll(getResourceIconsForUsingTemplateIso(noVmIconResponses)); + for (UserVmResponse vmResponse : responses) { + ResourceIcon icon = vmIcons.get(vmResponse.getId()); + if (icon == null) { + continue; + } + ResourceIconResponse iconResponse = _responseGenerator.createResourceIconResponse(icon); vmResponse.setResourceIconResponse(iconResponse); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java index 0f5dade96d2..7906ec632a4 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java @@ -16,20 +16,19 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.uservm.UserVm; import com.cloud.utils.exception.CloudRuntimeException; - -import org.apache.cloudstack.api.ApiArgValidator; -import org.apache.cloudstack.api.response.UserDataResponse; - +import com.cloud.utils.net.Dhcp; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -40,15 +39,17 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; +import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.user.Account; -import com.cloud.uservm.UserVm; -import com.cloud.utils.net.Dhcp; -import com.cloud.vm.VirtualMachine; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " + "new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " + @@ -154,6 +155,14 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, " autoscaling groups or CKS, delete protection will be ignored.") private Boolean deleteProtection; + @Parameter(name = ApiConstants.INSTANCE_LEASE_DURATION, type = CommandType.INTEGER, since = "4.21.0", + description = "Number of days to lease the instance from now onward. Use -1 to remove the existing lease") + private Integer leaseDuration; + + @Parameter(name = ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION, type = CommandType.STRING, since = "4.21.0", + description = "Lease expiry action, valid values are STOP and DESTROY") + private String leaseExpiryAction; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -324,4 +333,21 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction, public ApiCommandResourceType getApiResourceType() { return ApiCommandResourceType.VirtualMachine; } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public VMLeaseManager.ExpiryAction getLeaseExpiryAction() { + if (StringUtils.isBlank(leaseExpiryAction)) { + return null; + } + VMLeaseManager.ExpiryAction action = EnumUtils.getEnumIgnoreCase(VMLeaseManager.ExpiryAction.class, leaseExpiryAction); + if (action == null) { + throw new InvalidParameterValueException("Invalid value configured for leaseexpiryaction, valid values are: " + + com.cloud.utils.EnumUtils.listValues(VMLeaseManager.ExpiryAction.values())); + } + return action; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java index 9445aba23c0..6ddb5f1e567 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.volume; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; @@ -114,12 +115,15 @@ public class ExtractVolumeCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "Extraction job"; + String volumeId = this._uuidMgr.getUuid(Volume.class, getId()); + String zoneId = this._uuidMgr.getUuid(DataCenter.class, getZoneId()); + + return String.format("Extracting volume: %s from zone: %s", volumeId, zoneId); } @Override public void execute() { - CallContext.current().setEventDetails("Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId())); + CallContext.current().setEventDetails(getEventDescription()); String uploadUrl = _volumeService.extractVolume(this); if (uploadUrl != null) { ExtractResponse response = _responseGenerator.createVolumeExtractResponse(id, zoneId, getEntityOwnerId(), mode, uploadUrl); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java index b28c02cb800..c4b5e159b5c 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateStaticRouteCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.PrivateGatewayResponse; import org.apache.cloudstack.api.response.StaticRouteResponse; +import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; import com.cloud.event.EventTypes; @@ -45,20 +46,40 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.GATEWAY_ID, type = CommandType.UUID, entityType = PrivateGatewayResponse.class, - required = true, - description = "the gateway id we are creating static route for") + description = "the gateway id we are creating static route for. Mutually exclusive with the nexthop parameter") private Long gatewayId; + @Parameter(name = ApiConstants.VPC_ID, + type = CommandType.UUID, + entityType = VpcResponse.class, + description = "the vpc id for which the static route is created. This is required for nexthop parameter", + since = "4.21.0") + private Long vpcId; + + @Parameter(name = ApiConstants.NEXT_HOP, + type = CommandType.STRING, + description = "the next hop of static route. Mutually exclusive with the gatewayid parameter", + since = "4.21.0") + private String nextHop; + @Parameter(name = ApiConstants.CIDR, required = true, type = CommandType.STRING, description = "static route cidr") private String cidr; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public long getGatewayId() { + public Long getGatewayId() { return gatewayId; } + public Long getVpcId() { + return vpcId; + } + + public String getNextHop() { + return nextHop; + } + public String getCidr() { return cidr; } @@ -69,7 +90,7 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { try { - StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getCidr()); + StaticRoute result = _vpcService.createStaticRoute(getGatewayId(), getVpcId(), getNextHop(), getCidr()); setEntityId(result.getId()); setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException ex) { @@ -114,11 +135,8 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid gateway id is specified"); - } - return _entityMgr.findById(Vpc.class, gateway.getVpcId()).getAccountId(); + Long vpcId = getSyncObjId(); + return _entityMgr.findById(Vpc.class, vpcId).getAccountId(); } @Override @@ -128,11 +146,20 @@ public class CreateStaticRouteCmd extends BaseAsyncCreateCmd { @Override public Long getSyncObjId() { - VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); - if (gateway == null) { - throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + if (gatewayId != null) { + VpcGateway gateway = _entityMgr.findById(VpcGateway.class, gatewayId); + if (gateway == null) { + throw new InvalidParameterValueException("Invalid id is specified for the gateway"); + } + return gateway.getVpcId(); + } else if (vpcId != null) { + Vpc vpc = _entityMgr.findById(Vpc.class, vpcId); + if (vpc == null) { + throw new InvalidParameterValueException("Invalid vpc id is specified"); + } + return vpc.getId(); } - return gateway.getVpcId(); + throw new InvalidParameterValueException("One of vpcId or gatewayId must be specified"); } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index 2f62d0d7210..08b7b9911b1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vpc; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.acl.RoleType; @@ -125,6 +126,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { @Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the VPC tiers") private Long asNumber; + @Parameter(name=ApiConstants.USE_VIRTUAL_ROUTER_IP_RESOLVER, type=CommandType.BOOLEAN, + description="(optional) for NSX based VPCs: when set to true, use the VR IP as nameserver, otherwise use DNS1 and DNS2") + private Boolean useVrIpResolver; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -205,6 +210,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return asNumber; } + public boolean getUseVrIpResolver() { + return BooleanUtils.toBoolean(useVrIpResolver); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java index 6f31176c4ff..6c3f57b3dfa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnGatewayCmd.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.Site2SiteVpnGatewayResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.context.CallContext; @@ -44,9 +45,16 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { type = CommandType.UUID, entityType = VpcResponse.class, required = true, - description = "public ip address id of the vpn gateway") + description = "id of the vpc") private Long vpcId; + @Parameter(name = ApiConstants.IP_ADDRESS_ID, + type = CommandType.UUID, + entityType = IPAddressResponse.class, + description = "the public IP address ID for which VPN gateway is being enabled. By default the source NAT IP or router IP will be used.", + since = "4.21.0") + private Long ipAddressId; + @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vpn to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; @@ -58,6 +66,10 @@ public class CreateVpnGatewayCmd extends BaseAsyncCreateCmd { return vpcId; } + public Long getIpAddressId() { + return ipAddressId; + } + @Deprecated public Boolean getDisplay() { return display; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java index d926257437e..36ce23cbc6e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/zone/ListZonesCmd.java @@ -34,8 +34,6 @@ import org.apache.cloudstack.api.response.ZoneResponse; requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class ListZonesCmd extends BaseListCmd implements UserCmd { - private static final String s_name = "listzonesresponse"; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -69,6 +67,11 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { @Parameter(name = ApiConstants.SHOW_RESOURCE_ICON, type = CommandType.BOOLEAN, description = "flag to display the resource image for the zones") private Boolean showIcon; + @Parameter(name = ApiConstants.STORAGE_ACCESS_GROUP, type = CommandType.STRING, + description = "the name of the storage access group", + since = "4.21.0") + private String storageAccessGroup; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,15 +112,22 @@ public class ListZonesCmd extends BaseListCmd implements UserCmd { return showIcon != null ? showIcon : false; } + public String getStorageAccessGroup() { + return storageAccessGroup; + } + + public ListZonesCmd() { + + } + + public ListZonesCmd(String storageAccessGroup) { + this.storageAccessGroup = storageAccessGroup; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// - @Override - public String getCommandName() { - return s_name; - } - @Override public void execute() { ListResponse response = _queryService.listDataCenters(this); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/utils/OfferingUtils.java b/api/src/main/java/org/apache/cloudstack/api/command/utils/OfferingUtils.java new file mode 100644 index 00000000000..433a37c07cd --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/utils/OfferingUtils.java @@ -0,0 +1,38 @@ +// 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.utils; + +import com.cloud.offering.NetworkOffering; + +public class OfferingUtils { + + private OfferingUtils() { + } + + public static boolean isNetrisNatted(String provider, String networkMode) { + return "Netris".equalsIgnoreCase(provider) && + NetworkOffering.NetworkMode.NATTED.name().equalsIgnoreCase(networkMode); + } + + public static boolean isNsxWithoutLb(String provider, boolean nsxSupportsLbService) { + return "Nsx".equalsIgnoreCase(provider) && !nsxSupportsLbService; + } + + public static boolean isNetrisRouted(String provider, String networkMode) { + return "Netris".equalsIgnoreCase(provider) && NetworkOffering.NetworkMode.ROUTED.name().equalsIgnoreCase(networkMode); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java index aaad7f985fc..3c99e2cbec6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AccountResponse.java @@ -231,6 +231,18 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the total memory (in MB) available to be created for this account", since = "4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") + @Param(description = "the total number of gpus the account can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by account", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this account", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description = "the total primary storage space (in GiB) the account can own", since = "4.2.0") private String primaryStorageLimit; @@ -489,6 +501,21 @@ public class AccountResponse extends BaseResponse implements ResourceLimitAndCou this.vmRunning = vmRunning; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + public void setState(String state) { this.state = state; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java index 5b47a7a06e4..edbc94bc498 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/AsyncJobResponse.java @@ -55,10 +55,6 @@ public class AsyncJobResponse extends BaseResponse { @Param(description = "the async command executed") private String cmd; - @SerializedName("jobstatus") - @Param(description = "the current job status-should be 0 for PENDING") - private Integer jobStatus; - @SerializedName("jobprocstatus") @Param(description = "the progress information of the PENDING job") private Integer jobProcStatus; @@ -123,11 +119,6 @@ public class AsyncJobResponse extends BaseResponse { this.cmd = cmd; } - @Override - public void setJobStatus(Integer jobStatus) { - this.jobStatus = jobStatus; - } - public void setJobProcStatus(Integer jobProcStatus) { this.jobProcStatus = jobProcStatus; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java index 480ebcfb13d..0e895fa4e96 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupOfferingResponse.java @@ -41,6 +41,10 @@ public class BackupOfferingResponse extends BaseResponse { @Param(description = "description for the backup offering") private String description; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "provider name", since = "4.21.0") + private String provider; + @SerializedName(ApiConstants.EXTERNAL_ID) @Param(description = "external ID on the provider side") private String externalId; @@ -69,6 +73,10 @@ public class BackupOfferingResponse extends BaseResponse { this.externalId = externalId; } + public void setProvider(String provider) { + this.provider = provider; + } + public void setName(String name) { this.name = name; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java index 3847176608c..0db51f04034 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java @@ -57,10 +57,6 @@ public class BackupRepositoryResponse extends BaseResponse { @Param(description = "backup type") private String type; - @SerializedName(ApiConstants.MOUNT_OPTIONS) - @Param(description = "mount options for the backup repository") - private String mountOptions; - @SerializedName(ApiConstants.CAPACITY_BYTES) @Param(description = "capacity of the backup repository") private Long capacityBytes; @@ -112,14 +108,6 @@ public class BackupRepositoryResponse extends BaseResponse { this.address = address; } - public String getMountOptions() { - return mountOptions; - } - - public void setMountOptions(String mountOptions) { - this.mountOptions = mountOptions; - } - public String getProviderName() { return providerName; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java index 63419680fea..0ae558ac803 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupResponse.java @@ -26,6 +26,7 @@ import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; import java.util.Date; +import java.util.Map; @EntityReference(value = Backup.class) public class BackupResponse extends BaseResponse { @@ -34,6 +35,14 @@ public class BackupResponse extends BaseResponse { @Param(description = "ID of the VM backup") private String id; + @SerializedName(ApiConstants.NAME) + @Param(description = "name of the backup", since = "4.21.0") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "description for the backup", since = "4.21.0") + private String description; + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) @Param(description = "ID of the VM") private String vmId; @@ -102,6 +111,18 @@ public class BackupResponse extends BaseResponse { @Param(description = "zone name") private String zone; + @SerializedName(ApiConstants.VM_DETAILS) + @Param(description = "Lists the vm specific details for the backup", since = "4.21.0") + private Map vmDetails; + + @SerializedName(ApiConstants.INTERVAL_TYPE) + @Param(description = "Interval type of the backup", since = "4.21.0") + private String intervalType; + + @SerializedName(ApiConstants.BACKUP_VM_OFFERING_REMOVED) + @Param(description = "The backup offering corresponding to this backup was removed from the VM", since = "4.21.0") + private Boolean vmOfferingRemoved; + public String getId() { return id; } @@ -110,6 +131,22 @@ public class BackupResponse extends BaseResponse { this.id = id; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public String getVmId() { return vmId; } @@ -245,4 +282,28 @@ public class BackupResponse extends BaseResponse { public void setZone(String zone) { this.zone = zone; } + + public Map getVmDetails() { + return vmDetails; + } + + public void setVmDetails(Map vmDetails) { + this.vmDetails = vmDetails; + } + + public String getIntervalType() { + return this.intervalType; + } + + public void setIntervalType(String intervalType) { + this.intervalType = intervalType; + } + + public Boolean getVmOfferingRemoved() { + return this.vmOfferingRemoved; + } + + public void setVmOfferingRemoved(Boolean vmOfferingRemoved) { + this.vmOfferingRemoved = vmOfferingRemoved; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java index d7c6f96add5..d8c2980774d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupScheduleResponse.java @@ -28,6 +28,9 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = BackupSchedule.class) public class BackupScheduleResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the backup schedule.", since = "4.21.0") + private String id; @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) @Param(description = "name of the VM") @@ -51,7 +54,15 @@ public class BackupScheduleResponse extends BaseResponse { @SerializedName(ApiConstants.MAX_BACKUPS) @Param(description = "maximum number of backups retained") - private Integer maxBakups; + private Integer maxBackups; + + public void setId(String id) { + this.id = id; + } + + @SerializedName(ApiConstants.QUIESCE_VM) + @Param(description = "quiesce the instance before checkpointing the disks for backup") + private Boolean quiesceVM; public String getVmName() { return vmName; @@ -93,7 +104,11 @@ public class BackupScheduleResponse extends BaseResponse { this.timezone = timezone; } - public void setMaxBakups(Integer maxBakups) { - this.maxBakups = maxBakups; + public void setMaxBackups(Integer maxBackups) { + this.maxBackups = maxBackups; + } + + public void setQuiesceVM(Boolean quiesceVM) { + this.quiesceVM = quiesceVM; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index 3861ac455ed..eb0daf75148 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.response; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -136,6 +137,18 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0") private Integer sharedFsVmMinRamSize; + @SerializedName(ApiConstants.INSTANCE_LEASE_ENABLED) + @Param(description = "true if instance lease feature is enabled", since = "4.21.0") + private Boolean instanceLeaseEnabled; + + @SerializedName(ApiConstants.EXTENSIONS_PATH) + @Param(description = "The path of the extensions directory", since = "4.21.0", authorized = {RoleType.Admin}) + private String extensionsPath; + + @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED) + @Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0") + private Boolean dynamicScalingEnabled; + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } @@ -247,4 +260,16 @@ public class CapabilitiesResponse extends BaseResponse { public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) { this.sharedFsVmMinRamSize = sharedFsVmMinRamSize; } + + public void setInstanceLeaseEnabled(Boolean instanceLeaseEnabled) { + this.instanceLeaseEnabled = instanceLeaseEnabled; + } + + public void setExtensionsPath(String extensionsPath) { + this.extensionsPath = extensionsPath; + } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java index 4114c228e26..f399c1260f7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterDrsPlanMigrationResponse.java @@ -50,13 +50,13 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse { @Param(description = "Destination host for VM migration") String destHostName; - @SerializedName(ApiConstants.JOB_ID) + @SerializedName(ApiConstants.MIGRATION_JOB_ID) @Param(description = "id of VM migration async job") - private Long jobId; + private Long migrationJobId; - @SerializedName(ApiConstants.JOB_STATUS) + @SerializedName(ApiConstants.MIGRATION_JOB_STATUS) @Param(description = "Job status of VM migration async job") - private JobInfo.Status jobStatus; + private JobInfo.Status migrationJobStatus; public ClusterDrsPlanMigrationResponse(String vmId, String vmName, String srcHostId, String srcHostName, @@ -68,8 +68,8 @@ public class ClusterDrsPlanMigrationResponse extends BaseResponse { this.srcHostName = srcHostName; this.destHostId = destHostId; this.destHostName = destHostName; - this.jobId = jobId; - this.jobStatus = jobStatus; + this.migrationJobId = jobId; + this.migrationJobStatus = jobStatus; this.setObjectName(ApiConstants.MIGRATIONS); } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java index 1c69849239f..202ff4bd870 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ClusterResponse.java @@ -31,6 +31,8 @@ import com.google.gson.annotations.SerializedName; @EntityReference(value = Cluster.class) public class ClusterResponse extends BaseResponseWithAnnotations { + private transient long internalId; + @SerializedName(ApiConstants.ID) @Param(description = "the cluster ID") private String id; @@ -95,6 +97,34 @@ public class ClusterResponse extends BaseResponseWithAnnotations { @Param(description = "CPU Arch of the hosts in the cluster", since = "4.20") private String arch; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the host", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.POD_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the pod", since = "4.21.0") + private String podStorageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description="The ID of extension for this cluster", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description="The name of extension for this cluster", since = "4.21.0") + private String extensionName; + + public void setInternalId(long internalId) { + this.internalId = internalId; + } + + public long getInternalId() { + return internalId; + } + public String getId() { return id; } @@ -259,4 +289,44 @@ public class ClusterResponse extends BaseResponseWithAnnotations { public String getArch() { return arch; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getPodStorageAccessGroups() { + return podStorageAccessGroups; + } + + public void setPodStorageAccessGroups(String podStorageAccessGroups) { + this.podStorageAccessGroups = podStorageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getExtensionName() { + return extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ConsoleSessionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleSessionResponse.java new file mode 100644 index 00000000000..85747d7c2a8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ConsoleSessionResponse.java @@ -0,0 +1,236 @@ +// 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 org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import com.cloud.serializer.Param; +import org.apache.cloudstack.consoleproxy.ConsoleSession; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; + +@EntityReference(value = ConsoleSession.class) +public class ConsoleSessionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the console session.") + private String id; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the console session's endpoint was created.") + private Date created; + + @SerializedName(ApiConstants.DOMAIN) + @Param(description = "Domain of the account that created the console endpoint.") + private String domain; + + @SerializedName(ApiConstants.DOMAIN_PATH) + @Param(description = "Domain path of the account that created the console endpoint.") + private String domainPath; + + @SerializedName(ApiConstants.DOMAIN_ID) + @Param(description = "Domain ID of the account that created the console endpoint.") + private String domainId; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "Account that created the console endpoint.") + private String account; + + @SerializedName(ApiConstants.ACCOUNT_ID) + @Param(description = "ID of the account that created the console endpoint.") + private String accountId; + + @SerializedName(ApiConstants.USER) + @Param(description = "User that created the console endpoint.") + private String user; + + @SerializedName(ApiConstants.USER_ID) + @Param(description = "ID of the user that created the console endpoint.") + private String userId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "ID of the virtual machine.") + private String vmId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) + @Param(description = "Name of the virtual machine.") + private String vmName; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "ID of the host.") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "Name of the host.") + private String hostName; + + @SerializedName(ApiConstants.ACQUIRED) + @Param(description = "Date when the console session was acquired.") + private Date acquired; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Date when the console session was removed.") + private Date removed; + + @SerializedName(ApiConstants.CONSOLE_ENDPOINT_CREATOR_ADDRESS) + @Param(description = "IP address of the creator of the console endpoint.") + private String consoleEndpointCreatorAddress; + + @SerializedName(ApiConstants.CLIENT_ADDRESS) + @Param(description = "IP address of the client that created the console session.") + private String clientAddress; + + public void setId(String id) { + this.id = id; + } + + public void setCreated(Date created) { + this.created = created; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public void setDomainPath(String domainPath) { + this.domainPath = domainPath; + } + + public void setDomainId(String domainId) { + this.domainId = domainId; + } + + public void setAccount(String account) { + this.account = account; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public void setUser(String user) { + this.user = user; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public void setAcquired(Date acquired) { + this.acquired = acquired; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public void setConsoleEndpointCreatorAddress(String consoleEndpointCreatorAddress) { + this.consoleEndpointCreatorAddress = consoleEndpointCreatorAddress; + } + + public void setClientAddress(String clientAddress) { + this.clientAddress = clientAddress; + } + + public String getId() { + return id; + } + + public Date getCreated() { + return created; + } + + public String getDomain() { + return domain; + } + + public String getDomainPath() { + return domainPath; + } + + public String getDomainId() { + return domainId; + } + + public String getAccount() { + return account; + } + + public String getAccountId() { + return accountId; + } + + public String getUser() { + return user; + } + + public String getUserId() { + return userId; + } + + public String getVmId() { + return vmId; + } + + public String getVmName() { + return vmName; + } + + public String getHostId() { + return hostId; + } + + public String getHostName() { + return hostName; + } + + public Date getAcquired() { + return acquired; + } + + public Date getRemoved() { + return removed; + } + + public String getConsoleEndpointCreatorAddress() { + return consoleEndpointCreatorAddress; + } + + public String getClientAddress() { + return clientAddress; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java index c60917bbe7a..c1e04beee73 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CreateConsoleEndpointResponse.java @@ -26,7 +26,7 @@ public class CreateConsoleEndpointResponse extends BaseResponse { public CreateConsoleEndpointResponse() { } - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "true if the console endpoint is generated properly") private Boolean result; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java index 74fa2cbb1e4..e2246a1d531 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainResponse.java @@ -183,6 +183,15 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this domain", since="4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") @Param(description="the total number of gpus the domain can own", since="4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") @Param(description="the total number of gpus owned by domain", since="4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") @Param(description="the total number of gpus available to be created for this domain", since="4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description="the total primary storage space (in GiB) the domain can own", since="4.2.0") private String primaryStorageLimit; @@ -478,6 +487,21 @@ public class DomainResponse extends BaseResponseWithAnnotations implements Resou this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java index b23d0f4b527..4b5e886a16e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DomainRouterResponse.java @@ -245,6 +245,10 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements @Param(description = "the version of the code / software in the router") private String softwareVersion; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the router", since = "4.20.1") + private String arch; + public DomainRouterResponse() { nics = new LinkedHashSet(); } @@ -518,4 +522,8 @@ public class DomainRouterResponse extends BaseResponseWithAnnotations implements public void setSoftwareVersion(String softwareVersion) { this.softwareVersion = softwareVersion; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java new file mode 100644 index 00000000000..d627f8077dc --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionParameterResponse.java @@ -0,0 +1,58 @@ +// 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.response; + +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class ExtensionCustomActionParameterResponse extends BaseResponse { + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the parameter") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the parameter") + private String type; + + @SerializedName(ApiConstants.VALIDATION_FORMAT) + @Param(description = "Validation format for value of the parameter. Available for specific types") + private String validationFormat; + + @SerializedName(ApiConstants.VALUE_OPTIONS) + @Param(description = "Comma-separated list of options for value of the parameter") + private List valueOptions; + + @SerializedName(ApiConstants.REQUIRED) + @Param(description = "Whether the parameter is required or not") + private Boolean required; + + public ExtensionCustomActionParameterResponse(String name, String type, String validationFormat, List valueOptions, + boolean required) { + this.name = name; + this.type = type; + this.validationFormat = validationFormat; + this.valueOptions = valueOptions; + this.required = required; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java new file mode 100644 index 00000000000..96edf6d2fd8 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionCustomActionResponse.java @@ -0,0 +1,184 @@ +// 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.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.extension.ExtensionCustomAction; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = ExtensionCustomAction.class) +public class ExtensionCustomActionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the custom action") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the custom action") + private String description; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description = "ID of the extension that this custom action belongs to") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description = "Name of the extension that this custom action belongs to") + private String extensionName; + + @SerializedName(ApiConstants.RESOURCE_TYPE) + @Param(description = "Resource type for which the action is available") + private String resourceType; + + @SerializedName(ApiConstants.ALLOWED_ROLE_TYPES) + @Param(description = "List of role types allowed for the custom action") + private List allowedRoleTypes; + + @SerializedName(ApiConstants.SUCCESS_MESSAGE) + @Param(description = "Message that will be used on successful execution of the action") + private String successMessage; + + @SerializedName(ApiConstants.ERROR_MESSAGE) + @Param(description = "Message that will be used on failure during execution of the action") + private String errorMessage; + + @SerializedName(ApiConstants.TIMEOUT) + @Param(description = "Specifies the timeout in seconds to wait for the action to complete before failing") + private Integer timeout; + + @SerializedName(ApiConstants.ENABLED) + @Param(description = "Whether the custom action is enabled or not") + private Boolean enabled; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "Details of the custom action") + private Map details; + + @SerializedName(ApiConstants.PARAMETERS) + @Param(description = "List of the parameters for the action", responseObject = ExtensionCustomActionParameterResponse.class) + private List parameters; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the custom action") + private Date created; + + public ExtensionCustomActionResponse(String id, String name, String description) { + this.id = id; + this.name = name; + this.description = description; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getResourceType() { + return resourceType; + } + + public void setResourceType(String resourceType) { + this.resourceType = resourceType; + } + + public List getAllowedRoleTypes() { + return allowedRoleTypes; + } + + public void setAllowedRoleTypes(List allowedRoleTypes) { + this.allowedRoleTypes = allowedRoleTypes; + } + + public void setSuccessMessage(String successMessage) { + this.successMessage = successMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public void setTimeout(Integer timeout) { + this.timeout = timeout; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public List getParameters() { + return parameters; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Map getDetails() { + return details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java new file mode 100644 index 00000000000..aa370887b74 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResourceResponse.java @@ -0,0 +1,95 @@ +// 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.response; + +import org.apache.cloudstack.extension.ExtensionResourceMap; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import java.util.Date; +import java.util.Map; + +@EntityReference(value = ExtensionResourceMap.class) +public class ExtensionResourceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the resource associated with the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the resource associated with this mapping") + private String name; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the resource") + private String type; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "the details of the resource map") + private Map details; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the mapping") + private Date created; + + public ExtensionResourceResponse() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java new file mode 100644 index 00000000000..fdf1e87df50 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/ExtensionResponse.java @@ -0,0 +1,182 @@ +// 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.response; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.extension.Extension; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +@EntityReference(value = Extension.class) +public class ExtensionResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the extension") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the extension") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the extension") + private String description; + + @SerializedName(ApiConstants.TYPE) + @Param(description = "Type of the extension") + private String type; + + @SerializedName(ApiConstants.PATH) + @Param(description = "The path of the entry point fo the extension") + private String path; + + @SerializedName(ApiConstants.PATH_READY) + @Param(description = "True if the extension path is in ready state across management servers") + private Boolean pathReady; + + @SerializedName(ApiConstants.IS_USER_DEFINED) + @Param(description = "True if the extension is added by admin") + private Boolean userDefined; + + @SerializedName(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM) + @Parameter(description = "Only honored when type is Orchestrator. Whether prepare VM is needed or not") + private Boolean orchestratorRequiresPrepareVm; + + @SerializedName(ApiConstants.STATE) + @Param(description = "The state of the extension") + private String state; + + @SerializedName(ApiConstants.DETAILS) + @Param(description = "The details of the extension") + private Map details; + + @SerializedName(ApiConstants.RESOURCES) + @Param(description = "List of resources to which extension is registered to", responseObject = ExtensionResourceResponse.class) + private List resources; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Creation timestamp of the extension") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "Removal timestamp of the extension, if applicable") + private Date removed; + + public ExtensionResponse(String id, String name, String description, String type) { + this.id = id; + this.name = name; + this.description = description; + this.type = type; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public String getType() { + return type; + } + + public String getPath() { + return path; + } + + public Boolean isPathReady() { + return pathReady; + } + + public Boolean isUserDefined() { + return userDefined; + } + + public Boolean isOrchestratorRequiresPrepareVm() { + return orchestratorRequiresPrepareVm; + } + + public String getState() { + return state; + } + + public Map getDetails() { + return details; + } + + public void setPath(String path) { + this.path = path; + } + + public void setPathReady(Boolean pathReady) { + this.pathReady = pathReady; + } + + public void setUserDefined(Boolean userDefined) { + this.userDefined = userDefined; + } + + public void setOrchestratorRequiresPrepareVm(Boolean orchestratorRequiresPrepareVm) { + this.orchestratorRequiresPrepareVm = orchestratorRequiresPrepareVm; + } + + public void setState(String state) { + this.state = state; + } + + public void setDetails(Map details) { + this.details = details; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java new file mode 100644 index 00000000000..ad91b3490ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuCardResponse.java @@ -0,0 +1,109 @@ +// 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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; + +@EntityReference(value = GpuCard.class) +public class GpuCardResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU card") + protected String id; + + @SerializedName("deviceid") + @Param(description = "the device ID of the GPU card") + protected String deviceId; + + @SerializedName("devicename") + @Param(description = "the device name of the GPU card") + protected String deviceName; + + @SerializedName("name") + @Param(description = "the display name of the GPU card") + protected String name; + + @SerializedName("vendorname") + @Param(description = "the vendor name of the GPU card") + protected String vendorName; + + @SerializedName("vendorid") + @Param(description = "the vendor ID of the GPU card") + protected String vendorId; + + public GpuCardResponse(GpuCard gpuCard) { + super("gpucard"); + id = gpuCard.getUuid(); + deviceId = gpuCard.getDeviceId(); + deviceName = gpuCard.getDeviceName(); + name = gpuCard.getName(); + vendorName = gpuCard.getVendorName(); + vendorId = gpuCard.getVendorId(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java new file mode 100644 index 00000000000..09e98b54eaa --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GpuDeviceResponse.java @@ -0,0 +1,227 @@ +// 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.response; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuDevice; + +@EntityReference(value = GpuDevice.class) +public class GpuDeviceResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the GPU device") + private String id; + + @SerializedName(ApiConstants.BUS_ADDRESS) + @Param(description = "bus address of the GPU device or MDEV UUID for vGPU devices") + private String bussAddress; + + @SerializedName(ApiConstants.GPU_DEVICE_TYPE) + @Param(description = "bus address of the GPU device") + private GpuDevice.DeviceType type; + + @SerializedName(ApiConstants.HOST_ID) + @Param(description = "the host ID where the GPU device is attached") + private String hostId; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "the host name where the GPU device is attached") + private String hostName; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the GPU card ID associated with this GPU device") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the GPU card name associated with this GPU device") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_ID) + @Param(description = "the vGPU profile ID assigned to this GPU device") + private String vmId; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_NAME) + @Param(description = "the vGPU profile name assigned to this GPU device") + private String vmName; + + @SerializedName(ApiConstants.VIRTUAL_MACHINE_STATE) + @Param(description = "the state of the virtual machine to which this GPU device is allocated") + private VirtualMachine.State vmState; + + @SerializedName(ApiConstants.STATE) + @Param(description = "the vGPU profile name assigned to this GPU device") + private GpuDevice.State state; + + @SerializedName(ApiConstants.MANAGED_STATE) + @Param(description = "the managed state of the GPU device (Enabled/Disabled)") + private GpuDevice.ManagedState managedState; + + @SerializedName(ApiConstants.PARENT_GPU_DEVICE_ID) + @Param(description = "the ID of the parent GPU device, if this is a vGPU") + private String parentGpuDeviceId; + + @SerializedName(ApiConstants.NUMA_NODE) + @Param(description = "the NUMA node where the GPU device is located") + private String numaNode; + + + public GpuDeviceResponse() { + // Empty constructor for serialization + super("gpudevice"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBussAddress() { + return bussAddress; + } + + public void setBussAddress(String bussAddress) { + this.bussAddress = bussAddress; + } + + public GpuDevice.DeviceType getType() { + return type; + } + + public void setType(GpuDevice.DeviceType type) { + this.type = type; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public String getVmId() { + return vmId; + } + + public void setVmId(String vmId) { + this.vmId = vmId; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public GpuDevice.State getState() { + return state; + } + + public void setState(GpuDevice.State state) { + this.state = state; + } + + public GpuDevice.ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(GpuDevice.ManagedState managedState) { + this.managedState = managedState; + } + + public String getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(String parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java index 7872bf22085..495e349856a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuestOSCategoryResponse.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.response; +import java.util.Date; + import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.ApiConstants; @@ -26,7 +28,7 @@ import com.cloud.serializer.Param; import com.cloud.storage.GuestOsCategory; @EntityReference(value = GuestOsCategory.class) -public class GuestOSCategoryResponse extends BaseResponse { +public class GuestOSCategoryResponse extends BaseResponse implements SetResourceIconResponse { @SerializedName(ApiConstants.ID) @Param(description = "the ID of the OS category") private String id; @@ -35,6 +37,18 @@ public class GuestOSCategoryResponse extends BaseResponse { @Param(description = "the name of the OS category") private String name; + @SerializedName(ApiConstants.IS_FEATURED) + @Param(description = "Whether the OS category is featured", since = "4.21.0") + private Boolean featured; + + @SerializedName(ApiConstants.RESOURCE_ICON) + @Param(description = "Base64 string representation of the resource icon", since = "4.21.0") + private ResourceIconResponse resourceIconResponse; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "Date when the OS category was created." ) + private Date created; + public String getId() { return id; } @@ -50,4 +64,17 @@ public class GuestOSCategoryResponse extends BaseResponse { public void setName(String name) { this.name = name; } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + @Override + public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { + this.resourceIconResponse = resourceIconResponse; + } + + public void setCreated(Date created) { + this.created = created; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java new file mode 100644 index 00000000000..fe8a85b4176 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/GuiThemeResponse.java @@ -0,0 +1,179 @@ +//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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gui.theme.GuiThemeJoin; + +import java.util.Date; + +@EntityReference(value = {GuiThemeJoin.class}) +public class GuiThemeResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the custom GUI theme.") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the GUI theme.") + private String name; + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "Description of the GUI theme.") + private String description; + + @SerializedName(ApiConstants.CSS) + @Param(description = "The CSS to be retrieved and imported into the GUI when matching the theme access configurations.") + private String css; + + @SerializedName(ApiConstants.JSON_CONFIGURATION) + @Param(description = "The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.") + private String jsonConfiguration; + + @SerializedName(ApiConstants.COMMON_NAMES) + @Param(description = "A set of Common Names (CN) (fixed or wildcard) separated by comma that can retrieve the theme; e.g.: *acme.com,acme2.com") + private String commonNames; + + @SerializedName(ApiConstants.DOMAIN_IDS) + @Param(description = "A set of domain UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String domainIds; + + @SerializedName(ApiConstants.RECURSIVE_DOMAINS) + @Param(description = "Whether to consider the subdomains of the informed domain IDs.") + private Boolean recursiveDomains; + + @SerializedName(ApiConstants.ACCOUNT_IDS) + @Param(description = "A set of account UUIDs (also known as ID for the end-user) separated by comma that can retrieve the theme.") + private String accountIds; + + @SerializedName(ApiConstants.IS_PUBLIC) + @Param(description = "Defines whether a theme can be retrieved by anyone when only the `commonNames` is informed. If the `domainIds` or `accountIds` is informed, it is " + + "considered as `false`.") + private Boolean isPublic; + + @SerializedName(ApiConstants.CREATED) + @Param(description = "When the GUI theme was created.") + private Date created; + + @SerializedName(ApiConstants.REMOVED) + @Param(description = "When the GUI theme was removed.") + private Date removed; + + public GuiThemeResponse() { + super("guiThemes"); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getCss() { + return css; + } + + public void setCss(String css) { + this.css = css; + } + + public String getJsonConfiguration() { + return jsonConfiguration; + } + + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + public String getCommonNames() { + return commonNames; + } + + public void setCommonNames(String commonNames) { + this.commonNames = commonNames; + } + + public String getDomainIds() { + return domainIds; + } + + public void setDomainIds(String domainIds) { + this.domainIds = domainIds; + } + + public String getAccountIds() { + return accountIds; + } + + public void setAccountIds(String accountIds) { + this.accountIds = accountIds; + } + + public Boolean getPublic() { + return isPublic; + } + + public void setPublic(Boolean aPublic) { + isPublic = aPublic; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public Boolean getRecursiveDomains() { + return recursiveDomains; + } + + public void setRecursiveDomains(Boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java index c9a5c47887d..ca31bd8b155 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java @@ -90,7 +90,6 @@ public class HostResponse extends BaseResponseWithAnnotations { @SerializedName(ApiConstants.HYPERVISOR) @Param(description = "the host hypervisor") private String hypervisor; - @SerializedName("cpusockets") @Param(description = "the number of CPU sockets on the host") private Integer cpuSockets; @@ -152,7 +151,7 @@ public class HostResponse extends BaseResponseWithAnnotations { @Deprecated @SerializedName("memoryallocated") @Param(description = "the amount of the host's memory currently allocated") - private Long memoryAllocated; + private long memoryAllocated; @SerializedName("memoryallocatedpercentage") @Param(description = "the amount of the host's memory currently allocated in percentage") @@ -166,6 +165,14 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "the amount of the host's memory currently used") private Long memoryUsed; + @SerializedName("gputotal") + @Param(description = "Total GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuTotal; + + @SerializedName("gpuused") + @Param(description = "Used GPUs on the Host", responseObject = Long.class, since = "4.21") + private Long gpuUsed; + @SerializedName(ApiConstants.GPUGROUP) @Param(description = "GPU cards present in the host", responseObject = GpuResponse.class, since = "4.4") private List gpuGroup; @@ -198,6 +205,8 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "the management server name of the host", since = "4.21.0") private String managementServerName; + private transient long clusterInternalId; + @SerializedName("clusterid") @Param(description = "the cluster ID of the host") private String clusterId; @@ -302,6 +311,30 @@ public class HostResponse extends BaseResponseWithAnnotations { @Param(description = "CPU Arch of the host", since = "4.20") private String arch; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the host", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.CLUSTER_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the cluster", since = "4.21.0") + private String clusterStorageAccessGroups; + + @SerializedName(ApiConstants.POD_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the pod", since = "4.21.0") + private String podStorageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + + @SerializedName(ApiConstants.EXTENSION_ID) + @Param(description="The ID of extension for this cluster", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) + @Param(description="The name of extension for this cluster", since = "4.21.0") + private String extensionName; + @Override public String getObjectId() { return this.getId(); @@ -415,7 +448,7 @@ public class HostResponse extends BaseResponseWithAnnotations { this.memWithOverprovisioning=memWithOverprovisioning; } - public void setMemoryAllocated(Long memoryAllocated) { + public void setMemoryAllocated(long memoryAllocated) { this.memoryAllocated = memoryAllocated; } @@ -423,6 +456,14 @@ public class HostResponse extends BaseResponseWithAnnotations { this.memoryUsed = memoryUsed; } + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + public void setGpuUsed(Long gpuUsed) { + this.gpuUsed = gpuUsed; + } + public void setGpuGroup(List gpuGroup) { this.gpuGroup = gpuGroup; } @@ -455,6 +496,14 @@ public class HostResponse extends BaseResponseWithAnnotations { this.managementServerName = managementServerName; } + public long getClusterInternalId() { + return clusterInternalId; + } + + public void setClusterInternalId(long clusterInternalId) { + this.clusterInternalId = clusterInternalId; + } + public void setClusterId(String clusterId) { this.clusterId = clusterId; } @@ -491,6 +540,38 @@ public class HostResponse extends BaseResponseWithAnnotations { this.hostTags = hostTags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getClusterStorageAccessGroups() { + return clusterStorageAccessGroups; + } + + public void setClusterStorageAccessGroups(String clusterStorageAccessGroups) { + this.clusterStorageAccessGroups = clusterStorageAccessGroups; + } + + public String getPodStorageAccessGroups() { + return podStorageAccessGroups; + } + + public void setPodStorageAccessGroups(String podStorageAccessGroups) { + this.podStorageAccessGroups = podStorageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } + public String getExplicitHostTags() { return explicitHostTags; } @@ -703,8 +784,8 @@ public class HostResponse extends BaseResponseWithAnnotations { return memoryTotal; } - public Long getMemoryAllocated() { - return memoryAllocated == null ? 0 : memoryAllocated; + public long getMemoryAllocated() { + return memoryAllocated; } public void setMemoryAllocatedPercentage(String memoryAllocatedPercentage) { @@ -855,6 +936,14 @@ public class HostResponse extends BaseResponseWithAnnotations { return memoryAllocatedBytes; } + public Long getGpuTotal() { + return gpuTotal; + } + + public Long getGpuUsed() { + return gpuUsed; + } + public Boolean getTagARule() { return isTagARule; } @@ -894,4 +983,20 @@ public class HostResponse extends BaseResponseWithAnnotations { public Boolean getInstanceConversionSupported() { return instanceConversionSupported; } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } + + public String getExtensionName() { + return extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java index 0018edc8638..8b0e3cc0201 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -175,6 +175,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co @Param(description="true if range is dedicated for System VMs") private boolean forSystemVms; + @SerializedName(ApiConstants.FOR_PROVIDER) + @Param(description="true if range is dedicated for external network providers", since = "4.21.0") + private boolean forProvider; + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -332,4 +336,8 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co public void setForSystemVms(boolean forSystemVms) { this.forSystemVms = forSystemVms; } + + public void setForProvider(boolean forProvider) { + this.forProvider = forProvider; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java new file mode 100644 index 00000000000..cef5cdae2f4 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/KubernetesUserVmResponse.java @@ -0,0 +1,51 @@ +// 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.response; + +import com.cloud.network.router.VirtualRouter; +import com.cloud.serializer.Param; +import com.cloud.uservm.UserVm; +import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) +public class KubernetesUserVmResponse extends UserVmResponse { + @SerializedName(ApiConstants.IS_EXTERNAL_NODE) + @Param(description = "If the VM is an externally added node") + private boolean isExternalNode; + + @SerializedName(ApiConstants.IS_ETCD_NODE) + @Param(description = "If the VM is an etcd node") + private boolean isEtcdNode; + + @SerializedName(ApiConstants.KUBERNETES_NODE_VERSION) + @Param(description = "Kubernetes version of the node") + private String nodeVersion; + + + public void setExternalNode(boolean externalNode) { + isExternalNode = externalNode; + } + + public void setEtcdNode(boolean etcdNode) { + isEtcdNode = etcdNode; + } + + public void setNodeVersion(String nodeVersion) { this.nodeVersion = nodeVersion;} +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java index 729fb5ff3bc..8592a762c61 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java @@ -74,6 +74,11 @@ public class ManagementServerResponse extends BaseResponse { @Param(description = "the running OS kernel version for this Management Server") private String kernelVersion; + @Deprecated + @SerializedName(ApiConstants.SERVICE_IP) + @Param(description = "the IP Address for this Management Server. This is deprecated, please use 'ipaddress' instead.") + private String serviceIp; + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description = "the IP Address for this Management Server") private String ipAddress; @@ -82,6 +87,14 @@ public class ManagementServerResponse extends BaseResponse { @Param(description = "the Management Server Peers") private List peers; + @SerializedName(ApiConstants.LAST_AGENTS) + @Param(description = "the last agents this Management Server is responsible for, before shutdown or preparing for maintenance", since = "4.21.0.0") + private List lastAgents; + + @SerializedName(ApiConstants.AGENTS) + @Param(description = "the agents this Management Server is responsible for", since = "4.21.0.0") + private List agents; + @SerializedName(ApiConstants.AGENTS_COUNT) @Param(description = "the number of host agents this Management Server is responsible for", since = "4.21.0.0") private Long agentsCount; @@ -130,10 +143,22 @@ public class ManagementServerResponse extends BaseResponse { return lastBoot; } + public String getServiceIp() { + return serviceIp; + } + public String getIpAddress() { return ipAddress; } + public List getLastAgents() { + return lastAgents; + } + + public List getAgents() { + return agents; + } + public Long getAgentsCount() { return this.agentsCount; } @@ -186,10 +211,22 @@ public class ManagementServerResponse extends BaseResponse { this.kernelVersion = kernelVersion; } + public void setServiceIp(String serviceIp) { + this.serviceIp = serviceIp; + } + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } + public void setLastAgents(List lastAgents) { + this.lastAgents = lastAgents; + } + + public void setAgents(List agents) { + this.agents = agents; + } + public void setAgentsCount(Long agentsCount) { this.agentsCount = agentsCount; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java index db811ffbe2d..d5db8b4dabb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java @@ -28,12 +28,11 @@ import org.apache.cloudstack.api.BaseResponseWithAssociatedNetwork; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.Network; -import com.cloud.projects.ProjectAccount; import com.cloud.serializer.Param; import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") -@EntityReference(value = {Network.class, ProjectAccount.class}) +@EntityReference(value = {Network.class}) public class NetworkResponse extends BaseResponseWithAssociatedNetwork implements ControlledEntityResponse, SetResourceIconResponse { @SerializedName(ApiConstants.ID) @@ -212,14 +211,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "Name of the VPC to which this network belongs", since = "4.15") private String vpcName; - @SerializedName(ApiConstants.ASSOCIATED_NETWORK_ID) - @Param(description = "the ID of the Network associated with this network") - private String associatedNetworkId; - - @SerializedName(ApiConstants.ASSOCIATED_NETWORK) - @Param(description = "the name of the Network associated with this network") - private String associatedNetworkName; - @SerializedName(ApiConstants.TUNGSTEN_VIRTUAL_ROUTER_UUID) @Param(description = "Tungsten-Fabric virtual router the network belongs to") private String tungstenVirtualRouterUuid; @@ -620,14 +611,6 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement this.vpcName = vpcName; } - public void setAssociatedNetworkId(String associatedNetworkId) { - this.associatedNetworkId = associatedNetworkId; - } - - public void setAssociatedNetworkName(String associatedNetworkName) { - this.associatedNetworkName = associatedNetworkName; - } - @Override public void setResourceIconResponse(ResourceIconResponse icon) { this.icon = icon; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java index e4030799aa7..dcb93aaaf1d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ObjectStoreResponse.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.response; import com.cloud.serializer.Param; + +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.storage.object.ObjectStore; import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.api.BaseResponseWithAnnotations; @@ -24,15 +26,15 @@ import org.apache.cloudstack.api.EntityReference; @EntityReference(value = ObjectStore.class) public class ObjectStoreResponse extends BaseResponseWithAnnotations { - @SerializedName("id") + @SerializedName(ApiConstants.ID) @Param(description = "the ID of the object store") private String id; - @SerializedName("name") + @SerializedName(ApiConstants.NAME) @Param(description = "the name of the object store") private String name; - @SerializedName("url") + @SerializedName(ApiConstants.URL) @Param(description = "the url of the object store") private String url; @@ -44,6 +46,10 @@ public class ObjectStoreResponse extends BaseResponseWithAnnotations { @Param(description = "the total size of the object store") private Long storageTotal; + @SerializedName("storageallocated") + @Param(description = "the allocated size of the object store") + private Long storageAllocated; + @SerializedName("storageused") @Param(description = "the object store currently used size") private Long storageUsed; @@ -96,6 +102,14 @@ public class ObjectStoreResponse extends BaseResponseWithAnnotations { this.storageTotal = storageTotal; } + public Long getStorageAllocated() { + return storageAllocated; + } + + public void setStorageAllocated(Long storageAllocated) { + this.storageAllocated = storageAllocated; + } + public Long getStorageUsed() { return storageUsed; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java index 587fabfae8d..6a1afaecbcf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/PodResponse.java @@ -85,6 +85,14 @@ public class PodResponse extends BaseResponseWithAnnotations { @Param(description = "the capacity of the Pod", responseObject = CapacityResponse.class) private List capacities; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the pod", since = "4.21.0") + private String storageAccessGroups; + + @SerializedName(ApiConstants.ZONE_STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups on the zone", since = "4.21.0") + private String zoneStorageAccessGroups; + public String getId() { return id; } @@ -184,4 +192,20 @@ public class PodResponse extends BaseResponseWithAnnotations { public void setCapacities(List capacities) { this.capacities = capacities; } + + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + + public String getZoneStorageAccessGroups() { + return zoneStorageAccessGroups; + } + + public void setZoneStorageAccessGroups(String zoneStorageAccessGroups) { + this.zoneStorageAccessGroups = zoneStorageAccessGroups; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java index 8bdf042add0..e72f6b86036 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ProjectResponse.java @@ -116,6 +116,18 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou @Param(description = "the total memory (in MB) available to be created for this project", since = "4.2.0") private String memoryAvailable; + @SerializedName("gpulimit") + @Param(description = "the total number of gpus the project can own", since = "4.21.0") + private String gpuLimit; + + @SerializedName("gputotal") + @Param(description = "the total number of gpus owned by project", since = "4.21.0") + private Long gpuTotal; + + @SerializedName("gpuavailable") + @Param(description = "the total number of gpus available to be created for this project", since = "4.21.0") + private String gpuAvailable; + @SerializedName("primarystoragelimit") @Param(description = "the total primary storage space (in GiB) the project can own", since = "4.2.0") private String primaryStorageLimit; @@ -483,6 +495,21 @@ public class ProjectResponse extends BaseResponse implements ResourceLimitAndCou this.memoryAvailable = memoryAvailable; } + @Override + public void setGpuLimit(String gpuLimit) { + this.gpuLimit = gpuLimit; + } + + @Override + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + @Override + public void setGpuAvailable(String gpuAvailable) { + this.gpuAvailable = gpuAvailable; + } + @Override public void setPrimaryStorageLimit(String primaryStorageLimit) { this.primaryStorageLimit = primaryStorageLimit; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java index b86723b36c4..66de71dd763 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ResourceLimitAndCountResponse.java @@ -48,6 +48,12 @@ public interface ResourceLimitAndCountResponse { public void setMemoryAvailable(String memoryAvailable); + public void setGpuLimit(String gpuLimit); + + public void setGpuTotal(Long gpuTotal); + + public void setGpuAvailable(String gpuAvailable); + public void setPrimaryStorageLimit(String primaryStorageLimit); public void setPrimaryStorageTotal(Long primaryStorageTotal); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java index f98cf0acd5d..00f1e4e3bb0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/RouterHealthCheckResultResponse.java @@ -34,7 +34,7 @@ public class RouterHealthCheckResultResponse extends BaseResponse { @Param(description = "the type of the health check - basic or advanced") private String checkType; - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "result of the health check") private boolean result; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index 0622b936f6e..4565a878b34 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -80,7 +80,7 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if the vm needs to be volatile, i.e., on every reboot of vm from API root disk is discarded and creates a new root disk") private Boolean isVolatile; - @SerializedName("storagetags") + @SerializedName(ApiConstants.STORAGE_TAGS) @Param(description = "the tags for the service offering") private String tags; @@ -234,10 +234,58 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if virtual machine root disk will be encrypted on storage", since = "4.18") private Boolean encryptRoot; + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs to attach ", since = "4.21") + private Integer gpuCount; + + @SerializedName(ApiConstants.GPU_DISPLAY) + @Param(description = "whether GPU device is used for display or not ", since = "4.21") + private Boolean gpuDisplay; + @SerializedName(ApiConstants.PURGE_RESOURCES) @Param(description = "Whether to cleanup VM and its associated resource upon expunge", since = "4.20") private Boolean purgeResources; + @SerializedName(ApiConstants.INSTANCE_LEASE_DURATION) + @Param(description = "Instance lease duration (in days) for service offering", since = "4.21.0") + private Integer leaseDuration; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION) + @Param(description = "Action to be taken once lease is over", since = "4.21.0") + private String leaseExpiryAction; + public ServiceOfferingResponse() { } @@ -505,6 +553,22 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { this.cacheMode = cacheMode; } + public Integer getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseExpiryAction() { + return leaseExpiryAction; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + public String getVsphereStoragePolicy() { return vsphereStoragePolicy; } @@ -560,6 +624,86 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { public void setEncryptRoot(Boolean encrypt) { this.encryptRoot = encrypt; } + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } + public void setPurgeResources(Boolean purgeResources) { this.purgeResources = purgeResources; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotPolicyResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotPolicyResponse.java index bfa1cca1ca0..4ce77cfdf6e 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotPolicyResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotPolicyResponse.java @@ -16,17 +16,16 @@ // under the License. package org.apache.cloudstack.api.response; -import java.util.LinkedHashSet; -import java.util.Set; - +import com.cloud.serializer.Param; +import com.cloud.storage.snapshot.SnapshotPolicy; +import com.google.gson.annotations.SerializedName; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; -import com.cloud.serializer.Param; -import com.cloud.storage.snapshot.SnapshotPolicy; -import com.google.gson.annotations.SerializedName; +import java.util.LinkedHashSet; +import java.util.Set; @EntityReference(value = SnapshotPolicy.class) public class SnapshotPolicyResponse extends BaseResponseWithTagInformation { @@ -62,9 +61,14 @@ public class SnapshotPolicyResponse extends BaseResponseWithTagInformation { @Param(description = "The list of zones in which snapshot backup is scheduled", responseObject = ZoneResponse.class, since = "4.19.0") protected Set zones; + @SerializedName(ApiConstants.STORAGE) + @Param(description = "The list of pools in which snapshot backup is scheduled", responseObject = StoragePoolResponse.class, since = "4.21.0") + protected Set storagePools; + public SnapshotPolicyResponse() { tags = new LinkedHashSet(); zones = new LinkedHashSet<>(); + storagePools = new LinkedHashSet<>(); } public String getId() { @@ -130,4 +134,6 @@ public class SnapshotPolicyResponse extends BaseResponseWithTagInformation { public void setZones(Set zones) { this.zones = zones; } + + public void setStoragePools(Set pools) { this.storagePools = pools; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java index 9f7a7f42dec..5d6756c950d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java @@ -107,6 +107,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements @Param(description = "physical size of backedup snapshot on image store") private long physicalSize; + @SerializedName(ApiConstants.CHAIN_SIZE) + @Param(description = "chain size of snapshot including all parent snapshots. Shown only for incremental snapshots if snapshot.show.chain.size setting is set to true", since = "4.21.0") + private Long chainSize; + @SerializedName(ApiConstants.ZONE_ID) @Param(description = "id of the availability zone") private String zoneId; @@ -244,6 +248,10 @@ public class SnapshotResponse extends BaseResponseWithTagInformation implements this.physicalSize = physicalSize; } + public void setChainSize(long chainSize) { + this.chainSize = chainSize; + } + @Override public void setProjectId(String projectId) { this.projectId = projectId; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java index 51f8a130383..9008fce2392 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StaticRouteResponse.java @@ -42,9 +42,17 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit @Param(description = "VPC the static route belongs to") private String vpcId; - @SerializedName(ApiConstants.GATEWAY_ID) + @SerializedName(ApiConstants.VPC_GATEWAY_ID) @Param(description = "VPC gateway the route is created for") - private String gatewayId; + private String vpcGatewayId; + + @SerializedName(ApiConstants.VPC_GATEWAY_IP) + @Param(description = "IP of VPC gateway the route is created for", since = "4.21.0") + private String vpcGatewayIp; + + @SerializedName(ApiConstants.NEXT_HOP) + @Param(description = "Next hop of the static route", since = "4.21.0") + private String nextHop; @SerializedName(ApiConstants.CIDR) @Param(description = "static route CIDR") @@ -95,8 +103,16 @@ public class StaticRouteResponse extends BaseResponse implements ControlledEntit this.vpcId = vpcId; } - public void setGatewayId(String gatewayId) { - this.gatewayId = gatewayId; + public void setVpcGatewayId(String vpcGatewayId) { + this.vpcGatewayId = vpcGatewayId; + } + + public void setVpcGatewayIp(String vpcGatewayIp) { + this.vpcGatewayIp = vpcGatewayIp; + } + + public void setNextHop(String nextHop) { + this.nextHop = nextHop; } public void setCidr(String cidr) { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StatsResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StatsResponse.java index 5dd76fa5eef..287d78bb612 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StatsResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StatsResponse.java @@ -27,7 +27,7 @@ import com.google.gson.annotations.SerializedName; public class StatsResponse extends BaseResponse { @SerializedName("timestamp") - @Param(description = "the time when the VM stats were collected. The format is \"yyyy-MM-dd hh:mm:ss\"") + @Param(description = "the time when the VM stats were collected. The format is 'yyyy-MM-dd hh:mm:ss'") private Date timestamp; @SerializedName("cpuused") diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java new file mode 100644 index 00000000000..a6324dd62a9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/StorageAccessGroupResponse.java @@ -0,0 +1,108 @@ +// 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.response; + +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import com.cloud.serializer.Param; + +public class StorageAccessGroupResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the storage access group") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name of the storage access group") + private String name; + + @SerializedName("hosts") + @Param(description = "List of Hosts in the Storage Access Group") + private ListResponse hostResponseList; + + @SerializedName("clusters") + @Param(description = "List of Clusters in the Storage Access Group") + private ListResponse clusterResponseList; + + @SerializedName("pods") + @Param(description = "List of Pods in the Storage Access Group") + private ListResponse podResponseList; + + @SerializedName("zones") + @Param(description = "List of Zones in the Storage Access Group") + private ListResponse zoneResponseList; + + @SerializedName("storagepools") + @Param(description = "List of Storage Pools in the Storage Access Group") + private ListResponse storagePoolResponseList; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ListResponse getHostResponseList() { + return hostResponseList; + } + + public void setHostResponseList(ListResponse hostResponseList) { + this.hostResponseList = hostResponseList; + } + + public ListResponse getClusterResponseList() { + return clusterResponseList; + } + + public void setClusterResponseList(ListResponse clusterResponseList) { + this.clusterResponseList = clusterResponseList; + } + + public ListResponse getPodResponseList() { + return podResponseList; + } + + public void setPodResponseList(ListResponse podResponseList) { + this.podResponseList = podResponseList; + } + + public ListResponse getZoneResponseList() { + return zoneResponseList; + } + + public void setZoneResponseList(ListResponse zoneResponseList) { + this.zoneResponseList = zoneResponseList; + } + + public ListResponse getStoragePoolResponseList() { + return storagePoolResponseList; + } + + public void setStoragePoolResponseList(ListResponse storagePoolResponseList) { + this.storagePoolResponseList = storagePoolResponseList; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java index 676803ea86b..abc674ff0f9 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/StoragePoolResponse.java @@ -109,6 +109,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { @Param(description = "the tags for the storage pool") private String tags; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "the storage access groups for the storage pool", since = "4.21.0") + private String storageAccessGroups; + @SerializedName(ApiConstants.NFS_MOUNT_OPTIONS) @Param(description = "the nfs mount options for the storage pool", since = "4.19.1") private String nfsMountOpts; @@ -149,6 +153,10 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { @Param(description = "whether this pool is managed or not") private Boolean managed; + @SerializedName(ApiConstants.DETAILS) + @Param(description = "the storage pool details") + private Map details; + public Map getCaps() { return caps; } @@ -340,6 +348,14 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { this.tags = tags; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public Boolean getIsTagARule() { return isTagARule; } @@ -407,4 +423,12 @@ public class StoragePoolResponse extends BaseResponseWithAnnotations { public void setManaged(Boolean managed) { this.managed = managed; } + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java index 31a8b731491..7b1784fc767 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/SystemVmResponse.java @@ -38,14 +38,6 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the system VM type") private String systemVmType; - @SerializedName("jobid") - @Param(description = "the job ID associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private String jobId; - - @SerializedName("jobstatus") - @Param(description = "the job status associated with the system VM. This is only displayed if the router listed is part of a currently running asynchronous job.") - private Integer jobStatus; - @SerializedName("zoneid") @Param(description = "the Zone ID for the system VM") private String zoneId; @@ -186,6 +178,10 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { @Param(description = "the name of the service offering of the system virtual machine.") private String serviceOfferingName; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the system VM", since = "4.20.1") + private String arch; + @Override public String getObjectId() { return this.getId(); @@ -490,4 +486,8 @@ public class SystemVmResponse extends BaseResponseWithAnnotations { public void setServiceOfferingName(String serviceOfferingName) { this.serviceOfferingName = serviceOfferingName; } + + public void setArch(String arch) { + this.arch = arch; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java index 98e96091d8c..a94dbd95a56 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/TemplateResponse.java @@ -93,6 +93,8 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @Param(description = "the name of the OS type for this template.") private String osTypeName; + private transient Long osTypeCategoryId; + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description = "the account id to which the template belongs") private String accountId; @@ -208,6 +210,11 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements since = "4.15") private Boolean deployAsIs; + @SerializedName(ApiConstants.FOR_CKS) + @Param(description = "If true it indicates that the template can be used for CKS cluster deployments", + since = "4.21.0") + private Boolean forCks; + @SerializedName(ApiConstants.DEPLOY_AS_IS_DETAILS) @Param(description = "VMware only: additional key/value details tied with deploy-as-is template", since = "4.15") @@ -247,6 +254,12 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements @SerializedName(ApiConstants.USER_DATA_PARAMS) @Param(description="list of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in userdata", since = "4.18.0") private String userDataParams; + @SerializedName(ApiConstants.EXTENSION_ID) @Param(description="The ID of extension linked to this template", since = "4.21.0") + private String extensionId; + + @SerializedName(ApiConstants.EXTENSION_NAME) @Param(description="The name of extension linked to this template", since = "4.21.0") + private String extensionName; + public TemplateResponse() { tags = new LinkedHashSet<>(); } @@ -285,6 +298,14 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements this.osTypeName = osTypeName; } + public Long getOsTypeCategoryId() { + return osTypeCategoryId; + } + + public void setOsTypeCategoryId(Long osTypeCategoryId) { + this.osTypeCategoryId = osTypeCategoryId; + } + public void setId(String id) { this.id = id; } @@ -453,6 +474,10 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements this.deployAsIs = deployAsIs; } + public void setForCks(Boolean forCks) { + this.forCks = forCks; + } + public void setParentTemplateId(String parentTemplateId) { this.parentTemplateId = parentTemplateId; } @@ -528,4 +553,20 @@ public class TemplateResponse extends BaseResponseWithTagInformation implements public void setArch(String arch) { this.arch = arch; } + + public String getExtensionId() { + return extensionId; + } + + public void setExtensionId(String extensionId) { + this.extensionId = extensionId; + } + + public String getExtensionName() { + return extensionName; + } + + public void setExtensionName(String extensionName) { + this.extensionName = extensionName; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java index cec70f20cff..e9d45cb506a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanageVMInstanceResponse.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.api.BaseResponse; public class UnmanageVMInstanceResponse extends BaseResponse { - @SerializedName(ApiConstants.RESULT) + @SerializedName(ApiConstants.SUCCESS) @Param(description = "result of the unmanage VM operation") private boolean success; diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java index 7a26b178591..c1156f5f23a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UnmanagedInstanceResponse.java @@ -79,6 +79,14 @@ public class UnmanagedInstanceResponse extends BaseResponse { @Param(description = "the operating system of the virtual machine") private String operatingSystem; + @SerializedName(ApiConstants.BOOT_MODE) + @Param(description = "indicates the boot mode") + private String bootMode; + + @SerializedName(ApiConstants.BOOT_TYPE) + @Param(description = "indicates the boot type") + private String bootType; + @SerializedName(ApiConstants.DISK) @Param(description = "the list of disks associated with the virtual machine", responseObject = UnmanagedInstanceDiskResponse.class) private Set disks; @@ -211,4 +219,20 @@ public class UnmanagedInstanceResponse extends BaseResponse { public void addNic(NicResponse nic) { this.nics.add(nic); } + + public String getBootMode() { + return bootMode; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java index 2dfc66fa7d5..ce344596aeb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserDataResponse.java @@ -27,41 +27,41 @@ import org.apache.cloudstack.api.EntityReference; public class UserDataResponse extends BaseResponseWithAnnotations implements ControlledEntityResponse { @SerializedName(ApiConstants.ID) - @Param(description = "ID of the ssh keypair") + @Param(description = "ID of the User Data") private String id; @SerializedName(ApiConstants.NAME) - @Param(description = "Name of the userdata") + @Param(description = "Name of the User Data") private String name; - @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the userdata") + @SerializedName(ApiConstants.ACCOUNT_ID) @Param(description="the owner id of the User Data") private String accountId; - @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the userdata") + @SerializedName(ApiConstants.ACCOUNT) @Param(description="the owner of the User Data") private String accountName; @SerializedName(ApiConstants.PROJECT_ID) - @Param(description = "the project id of the userdata", since = "4.19.1") + @Param(description = "the project id of the User Data", since = "4.19.1") private String projectId; @SerializedName(ApiConstants.PROJECT) - @Param(description = "the project name of the userdata", since = "4.19.1") + @Param(description = "the project name of the User Data", since = "4.19.1") private String projectName; - @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the userdata owner") + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description="the domain id of the User Data owner") private String domainId; - @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the userdata owner") + @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the User Data owner") private String domain; @SerializedName(ApiConstants.DOMAIN_PATH) - @Param(description = "path of the domain to which the userdata owner belongs", since = "4.19.2.0") + @Param(description = "path of the domain to which the User Data owner belongs", since = "4.19.2.0") private String domainPath; - @SerializedName(ApiConstants.USER_DATA) @Param(description="base64 encoded userdata content") + @SerializedName(ApiConstants.USER_DATA) @Param(description="base64 encoded User Data content") private String userData; - @SerializedName(ApiConstants.PARAMS) @Param(description="list of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in userdata") + @SerializedName(ApiConstants.PARAMS) @Param(description="list of parameters which contains the list of keys or string parameters that are needed to be passed for any variables declared in the User Data") private String params; public UserDataResponse() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java index 1f4b493fba2..ca5bd09a9aa 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/UserVmResponse.java @@ -31,13 +31,13 @@ import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponseWithTagInformation; import org.apache.cloudstack.api.EntityReference; +import org.apache.commons.collections.CollectionUtils; import com.cloud.network.router.VirtualRouter; import com.cloud.serializer.Param; import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; import com.google.gson.annotations.SerializedName; -import org.apache.commons.collections.CollectionUtils; @SuppressWarnings("unused") @EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) @@ -182,6 +182,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "the name of the disk offering of the virtual machine. This parameter should not be used for retrieving disk offering details of DATA volumes. Use listVolumes API instead", since = "4.4") private String diskOfferingName; + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the gpu card to which service offering is linked", since = "4.21") + private String gpuCardName; + + @SerializedName(ApiConstants.VGPU_PROFILE_ID) + @Param(description = "the ID of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileId; + + @SerializedName(ApiConstants.VGPU_PROFILE_NAME) + @Param(description = "the name of the vgpu profile to which service offering is linked", since = "4.21") + private String vgpuProfileName; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + @SerializedName(ApiConstants.GPU_COUNT) + @Param(description = "the count of GPUs on the virtual machine", since = "4.21") + private Integer gpuCount; + @SerializedName(ApiConstants.BACKUP_OFFERING_ID) @Param(description = "the ID of the backup offering of the virtual machine", since = "4.14") private String backupOfferingId; @@ -392,10 +428,26 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co @Param(description = "VNF details", since = "4.19.0") private Map vnfDetails; - @SerializedName((ApiConstants.VM_TYPE)) + @SerializedName(ApiConstants.VM_TYPE) @Param(description = "User VM type", since = "4.20.0") private String vmType; + @SerializedName(ApiConstants.ARCH) + @Param(description = "CPU arch of the VM", since = "4.20.1") + private String arch; + + @SerializedName(ApiConstants.INSTANCE_LEASE_DURATION) + @Param(description = "Instance lease duration in days", since = "4.21.0") + private Integer leaseDuration; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_DATE) + @Param(description = "Instance lease expiry date", since = "4.21.0") + private Date leaseExpiryDate; + + @SerializedName(ApiConstants.INSTANCE_LEASE_EXPIRY_ACTION) + @Param(description = "Instance lease expiry action", since = "4.21.0") + private String leaseExpiryAction; + public UserVmResponse() { securityGroupList = new LinkedHashSet<>(); nics = new TreeSet<>(Comparator.comparingInt(x -> Integer.parseInt(x.getDeviceId()))); @@ -549,6 +601,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co return diskOfferingName; } + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public String getVgpuProfileId() { + return vgpuProfileId; + } + + public String getVgpuProfileName() { + return vgpuProfileName; + } + + public Long getVideoRam() { + return videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public Integer getGpuCount() { + return gpuCount; + } + public String getBackupOfferingId() { return backupOfferingId; } @@ -831,6 +919,42 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co this.diskOfferingName = diskOfferingName; } + public void setGpuCardId(String gpuCardId) { + this.gpuCardId = gpuCardId; + } + + public void setGpuCardName(String gpuCardName) { + this.gpuCardName = gpuCardName; + } + + public void setVgpuProfileId(String vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public void setVgpuProfileName(String vgpuProfileName) { + this.vgpuProfileName = vgpuProfileName; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + public void setBackupOfferingId(String backupOfferingId) { this.backupOfferingId = backupOfferingId; } @@ -1169,4 +1293,37 @@ public class UserVmResponse extends BaseResponseWithTagInformation implements Co public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + + public Integer getLeaseDuration() { + return leaseDuration; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public String getLeaseExpiryAction() { + return leaseExpiryAction; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + + public Date getLeaseExpiryDate() { + return leaseExpiryDate; + } + + public void setLeaseExpiryDate(Date leaseExpiryDate) { + this.leaseExpiryDate = leaseExpiryDate; + } + } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java new file mode 100644 index 00000000000..382b391ef59 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/response/VgpuProfileResponse.java @@ -0,0 +1,135 @@ +// 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.response; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.gpu.VgpuProfile; + +@EntityReference(value = VgpuProfile.class) +public class VgpuProfileResponse extends GpuCardResponse { + + @SerializedName(ApiConstants.DESCRIPTION) + @Param(description = "the description of the vGPU profile") + private String description; + + @SerializedName(ApiConstants.GPU_CARD_ID) + @Param(description = "the ID of the GPU card associated with this vGPU profile") + private String gpuCardId; + + @SerializedName(ApiConstants.GPU_CARD_NAME) + @Param(description = "the name of the vGPU profile") + private String gpuCardName; + + @SerializedName(ApiConstants.MAX_VGPU_PER_PHYSICAL_GPU) + @Param(description = "the maximum number of vGPUs per physical GPU") + private Long maxVgpuPerPgpu; + + @SerializedName(ApiConstants.VIDEORAM) + @Param(description = "the video RAM size in MB") + private Long videoRam; + + @SerializedName(ApiConstants.MAXHEADS) + @Param(description = "the maximum number of display heads") + private Long maxHeads; + + @SerializedName(ApiConstants.MAXRESOLUTIONX) + @Param(description = "the maximum X resolution") + private Long maxResolutionX; + + @SerializedName(ApiConstants.MAXRESOLUTIONY) + @Param(description = "the maximum Y resolution") + private Long maxResolutionY; + + public VgpuProfileResponse(VgpuProfile vgpuProfile, GpuCard gpuCard) { + super(gpuCard); + id = vgpuProfile.getUuid(); + name = vgpuProfile.getName(); + description = vgpuProfile.getDescription(); + gpuCardId = gpuCard.getUuid(); + gpuCardName = gpuCard.getName(); + maxVgpuPerPgpu = vgpuProfile.getMaxVgpuPerPgpu(); + videoRam = vgpuProfile.getVideoRam(); + maxHeads = vgpuProfile.getMaxHeads(); + maxResolutionX = vgpuProfile.getMaxResolutionX(); + maxResolutionY = vgpuProfile.getMaxResolutionY(); + setObjectName("vgpuprofile"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public String getGpuCardId() { + return gpuCardId; + } + + public String getGpuCardName() { + return gpuCardName; + } + + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java index 1492c23e882..2be08bb7fcb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java @@ -127,9 +127,9 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not") private Boolean forSystemVms; - @SerializedName(ApiConstants.FOR_NSX) - @Param(description = "indicates whether IP range is dedicated to NSX resources or not") - private Boolean forNsx; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "indicates to which provider the IP range is dedicated to", since = "4.21.0") + private String provider; public void setId(String id) { this.id = id; @@ -249,7 +249,7 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit this.ip6Cidr = ip6Cidr; } - public void setForNsx(Boolean forNsx) { - this.forNsx = forNsx; + public void setProvider(String provider) { + this.provider = provider; } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index 4a5279753a1..09e53dbb146 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -95,7 +95,15 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @SerializedName("securitygroupsenabled") @Param(description = "true if security groups support is enabled, false otherwise") - private boolean securityGroupsEnabled; + private Boolean securityGroupsEnabled; + + @SerializedName("gputotal") + @Param(description = "Total GPUs in the Zone", responseObject = Long.class, since = "4.21") + private Long gpuTotal; + + @SerializedName("gpuused") + @Param(description = "Used GPUs in the Zone", responseObject = Long.class, since = "4.21") + private Long gpuUsed; @SerializedName("allocationstate") @Param(description = "the allocation state of the cluster") @@ -115,7 +123,7 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @SerializedName(ApiConstants.LOCAL_STORAGE_ENABLED) @Param(description = "true if local storage offering enabled, false otherwise") - private boolean localStorageEnabled; + private Boolean localStorageEnabled; @SerializedName(ApiConstants.TAGS) @Param(description = "the list of resource tags associated with zone.", responseObject = ResourceTagResponse.class, since = "4.3") @@ -145,10 +153,15 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "the type of the zone - core or edge", since = "4.18.0") String type; + @Deprecated(since = "4.21.0") @SerializedName(ApiConstants.NSX_ENABLED) @Param(description = "true, if zone is NSX enabled", since = "4.20.0") private boolean nsxEnabled = false; + @SerializedName(ApiConstants.PROVIDER) + @Param(description = "External network provider if any", since = "4.21.0") + private String provider = null; + @SerializedName(ApiConstants.MULTI_ARCH) @Param(description = "true, if zone contains clusters and hosts from different CPU architectures", since = "4.20") private boolean multiArch; @@ -161,11 +174,19 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "true, if routed network/vpc is enabled", since = "4.20.1") private boolean routedModeEnabled = false; + @SerializedName(ApiConstants.STORAGE_ACCESS_GROUPS) + @Param(description = "comma-separated list of storage access groups for the zone", since = "4.21.0") + private String storageAccessGroups; + public ZoneResponse() { tags = new LinkedHashSet(); } + public ZoneResponse(Set tags) { + this.tags = tags; + } + public void setId(String id) { this.id = id; } @@ -218,6 +239,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso this.securityGroupsEnabled = securityGroupsEnabled; } + public void setGpuTotal(Long gpuTotal) { + this.gpuTotal = gpuTotal; + } + + public void setGpuUsed(Long gpuUsed) { + this.gpuUsed = gpuUsed; + } + public void setAllocationState(String allocationState) { this.allocationState = allocationState; } @@ -353,6 +382,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return securityGroupsEnabled; } + public Long getGpuUsed() { + return gpuUsed; + } + + public Long getGpuTotal() { + return gpuTotal; + } + public boolean isLocalStorageEnabled() { return localStorageEnabled; } @@ -373,6 +410,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return nsxEnabled; } + public String getProvider() { + return provider; + } + + public void setProvider(String provider) { + this.provider = provider; + } + @Override public void setResourceIconResponse(ResourceIconResponse resourceIconResponse) { this.resourceIconResponse = resourceIconResponse; @@ -402,6 +447,14 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso return type; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public void setNsxEnabled(boolean nsxEnabled) { this.nsxEnabled = nsxEnabled; } diff --git a/api/src/main/java/org/apache/cloudstack/backup/Backup.java b/api/src/main/java/org/apache/cloudstack/backup/Backup.java index dffe8a03213..951af9180e7 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/Backup.java +++ b/api/src/main/java/org/apache/cloudstack/backup/Backup.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.backup; import java.util.Date; import java.util.List; +import java.util.Map; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; @@ -33,28 +34,6 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { Allocated, Queued, BackingUp, BackedUp, Error, Failed, Restoring, Removed, Expunged } - public enum Type { - MANUAL, HOURLY, DAILY, WEEKLY, MONTHLY; - private int max = 8; - - public void setMax(int max) { - this.max = max; - } - - public int getMax() { - return max; - } - - @Override - public String toString() { - return this.name(); - } - - public boolean equals(String snapshotType) { - return this.toString().equalsIgnoreCase(snapshotType); - } - } - class Metric { private Long backupSize = 0L; private Long dataSize = 0L; @@ -85,6 +64,8 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { private String id; private Date created; private String type; + private Long backupSize = 0L; + private Long dataSize = 0L; public RestorePoint(String id, Date created, String type) { this.id = id; @@ -92,6 +73,12 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { this.type = type; } + public RestorePoint(String id, Date created, String type, Long backupSize, Long dataSize) { + this(id, created, type); + this.backupSize = backupSize; + this.dataSize = dataSize; + } + public String getId() { return id; } @@ -115,6 +102,22 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { public void setType(String type) { this.type = type; } + + public Long getBackupSize() { + return backupSize; + } + + public void setBackupSize(Long backupSize) { + this.backupSize = backupSize; + } + + public Long getDataSize() { + return dataSize; + } + + public void setDataSize(Long dataSize) { + this.dataSize = dataSize; + } } class VolumeInfo { @@ -122,12 +125,20 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { private Volume.Type type; private Long size; private String path; + private Long deviceId; + private String diskOfferingId; + private Long minIops; + private Long maxIops; - public VolumeInfo(String uuid, String path, Volume.Type type, Long size) { + public VolumeInfo(String uuid, String path, Volume.Type type, Long size, Long deviceId, String diskOfferingId, Long minIops, Long maxIops) { this.uuid = uuid; this.type = type; this.size = size; this.path = path; + this.deviceId = deviceId; + this.diskOfferingId = diskOfferingId; + this.minIops = minIops; + this.maxIops = maxIops; } public String getUuid() { @@ -150,13 +161,29 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { return size; } + public Long getDeviceId() { + return deviceId; + } + + public String getDiskOfferingId() { + return diskOfferingId; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + @Override public String toString() { - return StringUtils.join(":", uuid, path, type, size); + return StringUtils.join(":", uuid, path, type, size, deviceId, diskOfferingId, minIops, maxIops); } } - long getVmId(); + Long getVmId(); long getBackupOfferingId(); String getExternalId(); String getType(); @@ -164,6 +191,12 @@ public interface Backup extends ControlledEntity, InternalIdentity, Identity { Backup.Status getStatus(); Long getSize(); Long getProtectedSize(); + void setName(String name); + String getDescription(); + void setDescription(String description); List getBackedUpVolumes(); long getZoneId(); + Map getDetails(); + String getDetail(String name); + Long getBackupScheduleId(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java index cbd4b7e0596..c4b92fc9e05 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java @@ -18,19 +18,29 @@ package org.apache.cloudstack.backup; import java.util.List; +import java.util.Map; +import com.cloud.capacity.Capacity; import com.cloud.exception.ResourceAllocationException; import org.apache.cloudstack.api.command.admin.backup.ImportBackupOfferingCmd; import org.apache.cloudstack.api.command.admin.backup.UpdateBackupOfferingCmd; +import org.apache.cloudstack.api.command.user.backup.CreateBackupCmd; import org.apache.cloudstack.api.command.user.backup.CreateBackupScheduleCmd; +import org.apache.cloudstack.api.command.user.backup.DeleteBackupScheduleCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupOfferingsCmd; import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; +import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.storage.Volume; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.PluggableService; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VmDiskInfo; /** * Backup and Recover Manager Interface @@ -57,38 +67,6 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer "false", "Enable volume attach/detach operations for VMs that are assigned to Backup Offerings.", true); - ConfigKey BackupHourlyMax = new ConfigKey("Advanced", Integer.class, - "backup.max.hourly", - "8", - "Maximum recurring hourly backups to be retained for an instance. If the limit is reached, early backups from the start of the hour are deleted so that newer ones can be saved. This limit does not apply to manual backups. If set to 0, recurring hourly backups can not be scheduled.", - false, - ConfigKey.Scope.Global, - null); - - ConfigKey BackupDailyMax = new ConfigKey("Advanced", Integer.class, - "backup.max.daily", - "8", - "Maximum recurring daily backups to be retained for an instance. If the limit is reached, backups from the start of the day are deleted so that newer ones can be saved. This limit does not apply to manual backups. If set to 0, recurring daily backups can not be scheduled.", - false, - ConfigKey.Scope.Global, - null); - - ConfigKey BackupWeeklyMax = new ConfigKey("Advanced", Integer.class, - "backup.max.weekly", - "8", - "Maximum recurring weekly backups to be retained for an instance. If the limit is reached, backups from the beginning of the week are deleted so that newer ones can be saved. This limit does not apply to manual backups. If set to 0, recurring weekly backups can not be scheduled.", - false, - ConfigKey.Scope.Global, - null); - - ConfigKey BackupMonthlyMax = new ConfigKey("Advanced", Integer.class, - "backup.max.monthly", - "8", - "Maximum recurring monthly backups to be retained for an instance. If the limit is reached, backups from the beginning of the month are deleted so that newer ones can be saved. This limit does not apply to manual backups. If set to 0, recurring monthly backups can not be scheduled.", - false, - ConfigKey.Scope.Global, - null); - ConfigKey DefaultMaxAccountBackups = new ConfigKey("Account Defaults", Long.class, "max.account.backups", "20", @@ -137,6 +115,14 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer ConfigKey.Scope.Global, null); + ConfigKey BackupStorageCapacityThreshold = new ConfigKey<>("Alert", Float.class, + "zone.backupStorage.capacity.notificationthreshold", + "0.75", + "Percentage (as a value between 0 and 1) of backup storage utilization above which alerts will be sent about low storage available.", + true, + ConfigKey.Scope.Zone, + null); + /** * List backup provider offerings * @param zoneId zone id @@ -192,18 +178,18 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer /** * Deletes VM backup schedule for a VM - * @param vmId + * @param cmd * @return */ - boolean deleteBackupSchedule(Long vmId); + boolean deleteBackupSchedule(DeleteBackupScheduleCmd cmd); /** * Creates backup of a VM - * @param vmId Virtual Machine ID - * @param scheduleId Virtual Machine Backup Schedule ID + * @param cmd CreateBackupCmd + * @param job The async job associated with the backup retention * @return returns operation success */ - boolean createBackup(final Long vmId, final Long scheduleId) throws ResourceAllocationException; + boolean createBackup(CreateBackupCmd cmd, Object job) throws ResourceAllocationException; /** * List existing backups for a VM @@ -215,6 +201,15 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean restoreBackup(final Long backupId); + Map getIpToNetworkMapFromBackup(Backup backup, boolean preserveIps, List networkIds); + + Boolean canCreateInstanceFromBackup(Long backupId); + + /** + * Restore a backup to a new Instance + */ + boolean restoreBackupToVM(Long backupId, Long vmId) throws ResourceUnavailableException; + /** * Restore a backed up volume and attach it to a VM */ @@ -228,5 +223,25 @@ public interface BackupManager extends BackupService, Configurable, PluggableSer */ boolean deleteBackup(final Long backupId, final Boolean forced); + void validateBackupForZone(Long zoneId); + BackupOffering updateBackupOffering(UpdateBackupOfferingCmd updateBackupOfferingCmd); + + VmDiskInfo getRootDiskInfoFromBackup(Backup backup); + + List getDataDiskInfoListFromBackup(Backup backup); + + void checkVmDisksSizeAgainstBackup(List vmDiskInfoList, Backup backup); + + Map getBackupDetailsFromVM(VirtualMachine vm); + + String createVolumeInfoFromVolumes(List vmVolumes); + + String getBackupNameFromVM(VirtualMachine vm); + + BackupResponse createBackupResponse(Backup backup, Boolean listVmDetails); + + Capacity getBackupStorageUsedStats(Long zoneId); + + void checkAndRemoveBackupOfferingBeforeExpunge(VirtualMachine vm); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java index e3a6c3a62bd..1eb36f89556 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupProvider.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.backup; import java.util.List; -import java.util.Map; import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine; @@ -49,22 +48,21 @@ public interface BackupProvider { /** * Assign a VM to a backup offering or policy - * @param vm - * @param backup - * @param policy - * @return + * @param vm the machine to back up + * @param backupOffering the SLA definition for the backup + * @return succeeded? */ boolean assignVMToBackupOffering(VirtualMachine vm, BackupOffering backupOffering); /** * Removes a VM from a backup offering or policy - * @param vm - * @return + * @param vm the machine to stop backing up + * @return succeeded? */ boolean removeVMFromBackupOffering(VirtualMachine vm); /** - * Whether the provide will delete backups on removal of VM from the offfering + * Whether the provider will delete backups on removal of VM from the offering * @return boolean result */ boolean willDeleteBackupsOnOfferingRemoval(); @@ -72,19 +70,23 @@ public interface BackupProvider { /** * Starts and creates an adhoc backup process * for a previously registered VM backup - * @param backup - * @return + * + * @param vm the machine to make a backup of + * @param quiesceVM instance will be quiesced for checkpointing for backup. Applicable only to NAS plugin. + * @return the result and {code}Backup{code} {code}Object{code} */ - Pair takeBackup(VirtualMachine vm); + Pair takeBackup(VirtualMachine vm, Boolean quiesceVM); /** * Delete an existing backup - * @param backuo The backup to exclude + * @param backup The backup to exclude * @param forced Indicates if backup will be force removed or not - * @return + * @return succeeded? */ boolean deleteBackup(Backup backup, boolean forced); + boolean restoreBackupToVM(VirtualMachine vm, Backup backup, String hostIp, String dataStoreUuid); + /** * Restore VM from backup */ @@ -93,27 +95,44 @@ public interface BackupProvider { /** * Restore a volume from a backup */ - Pair restoreBackedUpVolume(Backup backup, String volumeUuid, String hostIp, String dataStoreUuid, Pair vmNameAndState); + Pair restoreBackedUpVolume(Backup backup, Backup.VolumeInfo backupVolumeInfo, String hostIp, String dataStoreUuid, Pair vmNameAndState); /** - * Returns backup metrics for a list of VMs in a zone - * @param zoneId - * @param vms - * @return + * Syncs backup metrics (backup size, protected size) from the plugin and stores it within the provider + * @param zoneId the zone for which to return metrics */ - Map getBackupMetrics(Long zoneId, List vms); + void syncBackupMetrics(Long zoneId); /** - * This method should TODO - * @param + * Returns a list of Backup.RestorePoint + * @param vm the machine to get the restore points for */ - public List listRestorePoints(VirtualMachine vm); + List listRestorePoints(VirtualMachine vm); /** - * This method should TODO - * @param - * @param - * @param metric + * Creates and returns an entry in the backups table by getting the information from restorePoint and vm. + * + * @param restorePoint the restore point to create a backup for + * @param vm The machine for which to create a backup */ - Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm, Backup.Metric metric); + Backup createNewBackupEntryForRestorePoint(Backup.RestorePoint restorePoint, VirtualMachine vm); + + /** + * Returns if the backup provider supports creating new instance from backup + */ + boolean supportsInstanceFromBackup(); + + /** + * Returns the backup storage usage (Used, Total) for a backup provider + * @param zoneId the zone for which to return metrics + * @return a pair of Used size and Total size for the backup storage + */ + Pair getBackupStorageStats(Long zoneId); + + /** + * Gets the backup storage usage (Used, Total) from the plugin and stores it in db + * @param zoneId the zone for which to return metrics + */ + void syncBackupStorageStats(Long zoneId); + } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java index 8e5c9740e69..be539a0eb04 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java @@ -28,7 +28,9 @@ public interface BackupRepository extends InternalIdentity, Identity { String getType(); String getAddress(); String getMountOptions(); + void setUsedBytes(Long usedBytes); Long getCapacityBytes(); Long getUsedBytes(); + void setCapacityBytes(Long capacityBytes); Date getCreated(); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java index 4ff946be9cd..b5138d34de1 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupSchedule.java @@ -30,5 +30,7 @@ public interface BackupSchedule extends InternalIdentity { String getTimezone(); Date getScheduledTimestamp(); Long getAsyncJobId(); - Integer getMaxBackups(); + Boolean getQuiesceVM(); + int getMaxBackups(); + String getUuid(); } diff --git a/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java new file mode 100644 index 00000000000..89ab97990df --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/command/ReconcileCommandService.java @@ -0,0 +1,65 @@ +// 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.command; + + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.hypervisor.Hypervisor; +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.Arrays; +import java.util.List; + +public interface ReconcileCommandService { + + ConfigKey ReconcileCommandsEnabled = new ConfigKey<>("Advanced", Boolean.class, + "reconcile.commands.enabled", "false", + "Indicates whether the background task to reconcile the commands is enabled or not", + false); + + ConfigKey ReconcileCommandsInterval = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.interval", "60", + "Interval (in seconds) for the background task to reconcile the commands", + false); + ConfigKey ReconcileCommandsMaxAttempts = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.max.attempts", "30", + "The maximum number of attempts to reconcile the commands", + true); + + ConfigKey ReconcileCommandsWorkers = new ConfigKey<>("Advanced", Integer.class, + "reconcile.commands.workers", "100", + "The Number of worker threads to reconcile the commands", + false); + + List SupportedHypervisorTypes = Arrays.asList(Hypervisor.HypervisorType.KVM); + + void persistReconcileCommands(Long hostId, Long requestSequence, Command[] cmd); + + boolean updateReconcileCommand(long requestSeq, Command command, Answer answer, Command.State newStateByManagement, Command.State newStateByAgent); + + void processCommand(Command pingCommand, Answer pingAnswer); + + void processAnswers(long requestSeq, Command[] commands, Answer[] answers); + + void updateReconcileCommandToInterruptedByManagementServerId(long managementServerId); + + void updateReconcileCommandToInterruptedByHostId(long hostId); + + boolean isReconcileResourceNeeded(long resourceId, ApiCommandResourceType resourceType); +} diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java index 23b571e7fae..655b8faf443 100644 --- a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java +++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleAccessManager.java @@ -18,6 +18,9 @@ package org.apache.cloudstack.consoleproxy; import com.cloud.utils.component.Manager; import org.apache.cloudstack.api.command.user.consoleproxy.ConsoleEndpoint; +import org.apache.cloudstack.api.command.user.consoleproxy.ListConsoleSessionsCmd; +import org.apache.cloudstack.api.response.ConsoleSessionResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import java.util.Date; @@ -48,4 +51,8 @@ public interface ConsoleAccessManager extends Manager, Configurable { String genAccessTicket(String host, String port, String sid, String tag, String sessionUuid); String genAccessTicket(String host, String port, String sid, String tag, Date normalizedHashTime, String sessionUuid); + + ListResponse listConsoleSessions(ListConsoleSessionsCmd cmd); + + ConsoleSession listConsoleSessionById(long id); } diff --git a/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java new file mode 100644 index 00000000000..6cbdd31fd94 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/consoleproxy/ConsoleSession.java @@ -0,0 +1,45 @@ +// 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.consoleproxy; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface ConsoleSession extends InternalIdentity, Identity { + + Date getCreated(); + + long getDomainId(); + + long getAccountId(); + + long getUserId(); + + long getInstanceId(); + + long getHostId(); + + Date getRemoved(); + + Date getAcquired(); + + String getConsoleEndpointCreatorAddress(); + + String getClientAddress(); +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java new file mode 100644 index 00000000000..33ff70fcace --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/CustomActionResultResponse.java @@ -0,0 +1,65 @@ +// 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.extension; + +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; + +public class CustomActionResultResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "ID of the action") + private String id; + + @SerializedName(ApiConstants.NAME) + @Param(description = "Name of the action") + private String name; + + @SerializedName(ApiConstants.SUCCESS) + @Param(description = "Whether custom action succeed or not") + private Boolean success; + + @SerializedName(ApiConstants.RESULT) + @Param(description = "Result of the action execution") + private Map result; + + public void setId(String id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + public void setSuccess(Boolean success) { + this.success = success; + } + + public Boolean getSuccess() { + return success; + } + + public void setResult(Map result) { + this.result = result; + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/Extension.java b/api/src/main/java/org/apache/cloudstack/extension/Extension.java new file mode 100644 index 00000000000..3068612ed6f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/Extension.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 org.apache.cloudstack.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface Extension extends InternalIdentity, Identity { + enum Type { + Orchestrator + } + enum State { + Enabled, Disabled; + }; + String getName(); + String getDescription(); + Type getType(); + String getRelativePath(); + boolean isPathReady(); + boolean isUserDefined(); + State getState(); + Date getCreated(); + + static String getDirectoryName(String name) { + return name.replaceAll("[^a-zA-Z0-9._-]", "_").toLowerCase(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java new file mode 100644 index 00000000000..776b42f671b --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionCustomAction.java @@ -0,0 +1,386 @@ +//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.extension; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.DateUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +public interface ExtensionCustomAction extends InternalIdentity, Identity { + enum ResourceType { + VirtualMachine(com.cloud.vm.VirtualMachine.class); + + private final Class clazz; + + ResourceType(Class clazz) { + this.clazz = clazz; + } + + public Class getAssociatedClass() { + return this.clazz; + } + } + + String getName(); + + String getDescription(); + + long getExtensionId(); + + ResourceType getResourceType(); + + Integer getAllowedRoleTypes(); + + String getSuccessMessage(); + + String getErrorMessage(); + + int getTimeout(); + + boolean isEnabled(); + + Date getCreated(); + + + class Parameter { + + public enum Type { + STRING(true), + NUMBER(true), + BOOLEAN(false), + DATE(false); + + private final boolean supportsOptions; + + Type(boolean supportsOptions) { + this.supportsOptions = supportsOptions; + } + + public boolean canSupportsOptions() { + return supportsOptions; + } + } + + public enum ValidationFormat { + // Universal default format + NONE(null), + + // String formats + UUID(Type.STRING), + EMAIL(Type.STRING), + PASSWORD(Type.STRING), + URL(Type.STRING), + + // Number formats + DECIMAL(Type.NUMBER); + + private final Type baseType; + + ValidationFormat(Type baseType) { + this.baseType = baseType; + } + + public Type getBaseType() { + return baseType; + } + } + + private static final Gson gson = new GsonBuilder() + .registerTypeAdapter(Parameter.class, new ParameterDeserializer()) + .setPrettyPrinting() + .create(); + + private final String name; + private final Type type; + private final ValidationFormat validationformat; + private final List valueoptions; + private final boolean required; + + public Parameter(String name, Type type, ValidationFormat validationformat, List valueoptions, boolean required) { + this.name = name; + this.type = type; + this.validationformat = validationformat; + this.valueoptions = valueoptions; + this.required = required; + } + + /** + * Parses a CSV string into a list of validated options. + */ + private static List parseValueOptions(String name, String csv, Type parsedType, ValidationFormat parsedFormat) { + if (StringUtils.isBlank(csv)) { + return null; + } + List values = Arrays.stream(csv.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + switch (parsedType) { + case STRING: + if (parsedFormat != null && parsedFormat != ValidationFormat.NONE) { + for (String value : values) { + if (!isValidStringValue(value, parsedFormat)) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + } + } + return new ArrayList<>(values); + case NUMBER: + try { + return values.stream() + .map(v -> parseNumber(v, parsedFormat)) + .collect(Collectors.toList()); + } catch (NumberFormatException ignored) { + throw new InvalidParameterValueException(String.format("Invalid value options with validation format: %s for parameter: %s", parsedFormat.name(), name)); + } + default: + throw new InvalidParameterValueException(String.format("Options not supported for type: %s for parameter: %s", parsedType, name)); + } + } + + private static Object parseNumber(String value, ValidationFormat parsedFormat) { + if (parsedFormat == ValidationFormat.DECIMAL) { + return Float.parseFloat(value); + } + return Integer.parseInt(value); + } + + private static boolean isValidStringValue(String value, ValidationFormat validationFormat ) { + switch (validationFormat) { + case NONE: + return true; + case UUID: + try { + UUID.fromString(value); + return true; + } catch (Exception ignored) { + return false; + } + case EMAIL: + return value.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"); + case PASSWORD: + return !value.trim().isEmpty(); + case URL: + try { + new java.net.URL(value); + return true; + } catch (Exception ignored) { + return false; + } + default: + return false; + } + } + + public static Parameter fromMap(Map map) throws InvalidParameterValueException { + final String name = map.get(ApiConstants.NAME); + final String typeStr = map.get(ApiConstants.TYPE); + final String validationFormatStr = map.get(ApiConstants.VALIDATION_FORMAT); + final String required = map.get(ApiConstants.REQUIRED); + final String valueOptionsStr = map.get(ApiConstants.VALUE_OPTIONS); + if (StringUtils.isBlank(name)) { + throw new InvalidParameterValueException("Invalid parameter specified with empty name"); + } + if (StringUtils.isBlank(typeStr)) { + throw new InvalidParameterValueException(String.format("No type specified for parameter: %s", name)); + } + Type parsedType = EnumUtils.getEnumIgnoreCase(Type.class, typeStr); + if (parsedType == null) { + throw new InvalidParameterValueException(String.format("Invalid type: %s specified for parameter: %s", + typeStr, name)); + } + ValidationFormat parsedFormat = StringUtils.isBlank(validationFormatStr) ? + ValidationFormat.NONE : EnumUtils.getEnumIgnoreCase(ValidationFormat.class, validationFormatStr); + if (parsedFormat == null || (!ValidationFormat.NONE.equals(parsedFormat) && + parsedFormat.getBaseType() != parsedType)) { + throw new InvalidParameterValueException( + String.format("Invalid validation format: %s specified for type: %s", + parsedFormat, parsedType.name())); + } + List valueOptions = parseValueOptions(name, valueOptionsStr, parsedType, parsedFormat); + return new Parameter(name, parsedType, parsedFormat, valueOptions, Boolean.parseBoolean(required)); + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public ValidationFormat getValidationFormat() { + return validationformat; + } + + public List getValueOptions() { + return valueoptions; + } + + public boolean isRequired() { + return required; + } + + @Override + public String toString() { + return String.format("Parameter %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, + ApiConstants.NAME, ApiConstants.TYPE, ApiConstants.REQUIRED)); + } + + public static String toJsonFromList(List parameters) { + return gson.toJson(parameters); + } + + public static List toListFromJson(String json) { + java.lang.reflect.Type listType = new TypeToken>() {}.getType(); + return gson.fromJson(json, listType); + } + + private void validateValueInOptions(Object value) { + if (CollectionUtils.isNotEmpty(valueoptions) && !valueoptions.contains(value)) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value + + ". Valid options are: " + valueoptions.stream().map(Object::toString).collect(Collectors.joining(", "))); + } + } + + public Object validatedValue(String value) { + if (StringUtils.isBlank(value)) { + throw new InvalidParameterValueException("Empty value for parameter '" + name + "': " + value); + } + try { + switch (type) { + case BOOLEAN: + if (!Arrays.asList("true", "false").contains(value)) { + throw new IllegalArgumentException(); + } + return Boolean.parseBoolean(value); + case DATE: + return DateUtil.parseTZDateString(value); + case NUMBER: + Object obj = parseNumber(value, validationformat); + validateValueInOptions(obj); + return obj; + default: + if (!isValidStringValue(value, validationformat)) { + throw new IllegalArgumentException(); + } + validateValueInOptions(value); + return value; + } + } catch (Exception e) { + throw new InvalidParameterValueException("Invalid value for parameter '" + name + "': " + value); + } + } + + public static Map validateParameterValues(List parameterDefinitions, + Map suppliedValues) throws InvalidParameterValueException { + if (suppliedValues == null) { + suppliedValues = new HashMap<>(); + } + for (Parameter param : parameterDefinitions) { + String value = suppliedValues.get(param.getName()); + if (param.isRequired()) { + if (value == null || value.trim().isEmpty()) { + throw new InvalidParameterValueException("Missing or empty required parameter: " + param.getName()); + } + } + } + Map validatedParams = new HashMap<>(); + for (Map.Entry entry : suppliedValues.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + Parameter param = parameterDefinitions.stream() + .filter(p -> p.getName().equals(name)) + .findFirst() + .orElse(null); + if (param != null) { + validatedParams.put(name, param.validatedValue(value)); + } else { + validatedParams.put(name, value); + } + } + return validatedParams; + } + + static class ParameterDeserializer implements JsonDeserializer { + + @Override + public Parameter deserialize(JsonElement json, java.lang.reflect.Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject obj = json.getAsJsonObject(); + String name = obj.get(ApiConstants.NAME).getAsString(); + String typeStr = obj.get(ApiConstants.TYPE).getAsString(); + String validationFormatStr = obj.has(ApiConstants.VALIDATION_FORMAT) ? obj.get(ApiConstants.VALIDATION_FORMAT).getAsString() : null; + boolean required = obj.has(ApiConstants.REQUIRED) && obj.get(ApiConstants.REQUIRED).getAsBoolean(); + + Parameter.Type typeEnum = Parameter.Type.valueOf(typeStr); + Parameter.ValidationFormat validationFormatEnum = (validationFormatStr != null) + ? Parameter.ValidationFormat.valueOf(validationFormatStr) + : Parameter.ValidationFormat.NONE; + + List valueOptions = null; + if (obj.has(ApiConstants.VALUE_OPTIONS) && obj.get(ApiConstants.VALUE_OPTIONS).isJsonArray()) { + JsonArray valueOptionsArray = obj.getAsJsonArray(ApiConstants.VALUE_OPTIONS); + valueOptions = new ArrayList<>(); + for (JsonElement el : valueOptionsArray) { + switch (typeEnum) { + case STRING: + valueOptions.add(el.getAsString()); + break; + case NUMBER: + if (validationFormatEnum == Parameter.ValidationFormat.DECIMAL) { + valueOptions.add(el.getAsFloat()); + } else { + valueOptions.add(el.getAsInt()); + } + break; + default: + throw new JsonParseException("Unsupported type for value options"); + } + } + } + + return new Parameter(name, typeEnum, validationFormatEnum, valueOptions, required); + } + } + } +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java new file mode 100644 index 00000000000..f50f841ed74 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionHelper.java @@ -0,0 +1,24 @@ +// 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.extension; + +public interface ExtensionHelper { + Long getExtensionIdForCluster(long clusterId); + Extension getExtension(long id); + Extension getExtensionForCluster(long clusterId); +} diff --git a/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java new file mode 100644 index 00000000000..40ebc19eb5e --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/extension/ExtensionResourceMap.java @@ -0,0 +1,34 @@ +//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.extension; + +import java.util.Date; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface ExtensionResourceMap extends InternalIdentity, Identity { + enum ResourceType { + Cluster + } + + long getExtensionId(); + long getResourceId(); + ResourceType getResourceType(); + Date getCreated(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java new file mode 100644 index 00000000000..2c02a0e30c2 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuCard.java @@ -0,0 +1,69 @@ +// 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.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * GPU card interface representing a physical GPU card model + */ +public interface GpuCard extends InternalIdentity, Identity { + /** + * @return the UUID of the GPU card + */ + String getUuid(); + + /** + * @return the device ID of the GPU card + */ + String getDeviceId(); + + /** + * @return the device name of the GPU card + */ + String getDeviceName(); + + /** + * @return the name of the GPU card + */ + String getName(); + + /** + * @return the vendor name of the GPU card + */ + String getVendorName(); + + /** + * @return the vendor ID of the GPU card + */ + String getVendorId(); + + /** + * @return the date when the GPU card was created + */ + Date getCreated(); + + + /** + * @return the group name of the GPU card based on how the XenServer expects it. + */ + String getGroupName(); + +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java new file mode 100644 index 00000000000..22adda46477 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuDevice.java @@ -0,0 +1,42 @@ +// 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.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +/** + * GPU device interface representing a physical GPU device + */ +public interface GpuDevice extends InternalIdentity, Identity { + + enum State { + Allocated, Free, Error, PartiallyAllocated, + } + + enum ManagedState { + Managed, Unmanaged, + } + + enum DeviceType { + PCI, MDEV, VGPUOnly, + } + + long getHostId(); + + State getState(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java new file mode 100644 index 00000000000..1e7928a319f --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/GpuService.java @@ -0,0 +1,157 @@ +// 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.gpu; + +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.agent.api.to.GPUDeviceTO; +import com.cloud.host.Host; +import com.cloud.utils.component.Manager; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.CreateVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DeleteVgpuProfileCmd; +import org.apache.cloudstack.api.command.admin.gpu.UnmanageGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.DiscoverGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.ManageGpuDeviceCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuDevicesCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuCardCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateGpuDeviceCmd; +import org.apache.cloudstack.api.command.admin.gpu.UpdateVgpuProfileCmd; +import org.apache.cloudstack.api.command.user.gpu.ListGpuCardsCmd; +import org.apache.cloudstack.api.command.user.gpu.ListVgpuProfilesCmd; +import org.apache.cloudstack.api.response.GpuCardResponse; +import org.apache.cloudstack.api.response.GpuDeviceResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.VgpuProfileResponse; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.HashMap; +import java.util.List; + +public interface GpuService extends Manager { + + ConfigKey GpuDetachOnStop = new ConfigKey<>(Boolean.class, "gpu.detach.on.stop", "Advanced", "false", + "Whether to detach GPU devices from VM on stop or keep them allocated", true, ConfigKey.Scope.Domain, null); + + GpuCard createGpuCard(CreateGpuCardCmd cmd); + + GpuCard updateGpuCard(UpdateGpuCardCmd cmd); + + boolean deleteGpuCard(DeleteGpuCardCmd cmd); + + VgpuProfileResponse createVgpuProfile(CreateVgpuProfileCmd cmd); + + VgpuProfileResponse updateVgpuProfile(UpdateVgpuProfileCmd cmd); + + boolean deleteVgpuProfile(DeleteVgpuProfileCmd cmd); + + ListResponse listGpuCards(ListGpuCardsCmd cmd); + + ListResponse listVgpuProfiles(ListVgpuProfilesCmd cmd); + + GpuDeviceResponse createGpuDevice(CreateGpuDeviceCmd cmd); + + GpuDeviceResponse updateGpuDevice(UpdateGpuDeviceCmd cmd); + + ListResponse listGpuDevices(ListGpuDevicesCmd cmd); + + boolean disableGpuDevice(UnmanageGpuDeviceCmd cmd); + + boolean enableGpuDevice(ManageGpuDeviceCmd cmd); + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateAllGpuDevicesForVm(long vmId); + + + /** + * Deallocate GPU devices for a VM on a host. + * + * @param vmId The ID of the VM to deallocate GPU devices for. + */ + void deallocateGpuDevicesForVmOnHost(long vmId, long hostId); + + /** + * Deallocate existing GPU devices for a VM on a host and allocate new GPU devices to the VM. + * + * @param vmId The ID of the VM to allocate GPU devices to. + * @param hostId The ID of the host to allocate GPU devices to. + * @param gpuDevices The list of GPU devices to allocate to the VM. + */ + void allocateGpuDevicesToVmOnHost(long vmId, long hostId, List gpuDevices); + + /** + * Discover GPU devices on a host by using the getGPUStatistics command and updating the GPU details for the host. + * + * @param cmd The command to discover GPU devices. + * @return The list of GPU devices. + */ + ListResponse discoverGpuDevices(DiscoverGpuDevicesCmd cmd); + + /** + * Check if GPU devices are available for a VM on a host by checking the number of available GPU devices for the + * vGPU profile. + * + * @param host The host to check GPU devices for. + * @param vmId The ID of the VM to check GPU devices for. + * @param vgpuProfile The vGPU profile to check GPU devices for. + * @param gpuCount The number of GPU devices to check for. + * @return True if GPU devices are available, false otherwise. + */ + boolean isGPUDeviceAvailable(Host host, Long vmId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Get GPU devices for a VM on a host by checking the number of available GPU devices for the vGPU profile. + * If the VM already has GPU devices assigned, deallocate them and allocate new GPU devices to the VM. + * The new GPU devices are allocated optimally to the VM. + * + * @param vm The VM to get GPU devices for. + * @param vgpuProfile The vGPU profile to get GPU devices for. + * @param gpuCount The number of GPU devices to get. + * @return The GPU devices. + */ + GPUDeviceTO getGPUDevice(VirtualMachine vm, long hostId, VgpuProfile vgpuProfile, int gpuCount); + + /** + * Gets the GPU group details from the GPU devices on a host. + * This fetches the GPU devices from the host and prepares the GPU group details for the host. + * The GPU group details are a map of GPU group name (Card's device name) to a map of vGPU profile name to + * VgpuTypesInfo. + * The VgpuTypesInfo contains the information about the GPU device. + * + * @param hostId The host ID to get GPU group details for. + * @return The GPU group details. + */ + HashMap> getGpuGroupDetailsFromGpuDevicesOnHost(long hostId); + + /** + * This method is used to add the GPU devices to the host when the host is discovered or when the GPU devices are + * updated. + * + * @param host The host to add the GPU devices to. + * @param newGpuDevicesInfo The list of GPU devices to add to the host. + */ + void addGpuDevicesToHost(Host host, List newGpuDevicesInfo); + + boolean deleteGpuDevices(DeleteGpuDeviceCmd deleteGpuDeviceCmd); +} diff --git a/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java new file mode 100644 index 00000000000..8cfac2a20de --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gpu/VgpuProfile.java @@ -0,0 +1,74 @@ +// 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.gpu; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +/** + * vGPU profile interface representing a virtualized GPU profile + */ +public interface VgpuProfile extends InternalIdentity, Identity { + /** + * @return the UUID of the vGPU profile + */ + String getUuid(); + + /** + * @return the name of the vGPU profile + */ + String getName(); + + /** + * @return the description of the vGPU profile + */ + String getDescription(); + + /** + * @return the date when the vGPU profile was created + */ + Date getCreated(); + + Long getCardId(); + + /** + * @return the maximum number of vGPUs per physical GPU + */ + Long getMaxVgpuPerPgpu(); + + /** + * @return the video RAM size in MB + */ + Long getVideoRam(); + + /** + * @return the maximum number of display heads + */ + Long getMaxHeads(); + + /** + * @return the maximum X resolution + */ + Long getMaxResolutionX(); + + /** + * @return the maximum Y resolution + */ + Long getMaxResolutionY(); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java new file mode 100644 index 00000000000..0cca8bc2d7d --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiTheme.java @@ -0,0 +1,61 @@ +// 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.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiTheme extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + Date getCreated(); + + Date getRemoved(); + + boolean getIsPublic(); + + void setId(Long id); + + void setUuid(String uuid); + + void setName(String name); + + void setDescription(String description); + + void setCss(String css); + + void setJsonConfiguration(String jsonConfiguration); + + void setCreated(Date created); + + void setRemoved(Date removed); + + void setIsPublic(boolean isPublic); + + boolean isRecursiveDomains(); + + void setRecursiveDomains(boolean recursiveDomains); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java new file mode 100644 index 00000000000..164535033d9 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetails.java @@ -0,0 +1,36 @@ +// 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.gui.theme; + +import org.apache.cloudstack.api.InternalIdentity; + +public interface GuiThemeDetails extends InternalIdentity { + + void setId(Long id); + + Long getGuiThemeId(); + + void setGuiThemeId(Long guiThemeId); + + String getType(); + + void setType(String type); + + String getValue(); + + void setValue(String value); +} diff --git a/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java new file mode 100644 index 00000000000..e54d53138ef --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoin.java @@ -0,0 +1,47 @@ +// 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.gui.theme; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +import java.util.Date; + +public interface GuiThemeJoin extends InternalIdentity, Identity { + + String getName(); + + String getDescription(); + + String getCss(); + + String getJsonConfiguration(); + + String getCommonNames(); + + String getDomains(); + + String getAccounts(); + + boolean isRecursiveDomains(); + + boolean getIsPublic(); + + Date getCreated(); + + Date getRemoved(); +} diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java similarity index 56% rename from plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java rename to api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java index 81c58ef27ba..8a066d3ffec 100644 --- a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/VmwareRequestResponse.java +++ b/api/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeService.java @@ -14,25 +14,22 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +package org.apache.cloudstack.gui.theme; -package org.apache.cloudstack.api.command.admin.zone; - -import com.cloud.serializer.Param; -import com.google.gson.annotations.SerializedName; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.command.user.gui.theme.CreateGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.ListGuiThemesCmd; +import org.apache.cloudstack.api.command.user.gui.theme.RemoveGuiThemeCmd; +import org.apache.cloudstack.api.command.user.gui.theme.UpdateGuiThemeCmd; +import org.apache.cloudstack.api.response.GuiThemeResponse; import org.apache.cloudstack.api.response.ListResponse; -public class VmwareRequestResponse extends ListResponse { - @SerializedName(ApiConstants.TOKEN) - @Param(description = "The Vmware API token to use for retrieving further responses with") - private String token; +public interface GuiThemeService { - public String getToken() { - return token; - } + ListResponse listGuiThemes(ListGuiThemesCmd cmd); - public void setToken(String token) { - this.token = token; - } + GuiThemeJoin createGuiTheme(CreateGuiThemeCmd cmd); + + GuiThemeJoin updateGuiTheme(UpdateGuiThemeCmd cmd); + + void removeGuiTheme(RemoveGuiThemeCmd cmd); } diff --git a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java index 221a550ad63..9285331f41a 100644 --- a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java +++ b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java @@ -158,7 +158,7 @@ public interface RoutedIpv4Manager extends PluggableService, Configurable { boolean isRoutedVpc(Vpc vpc); - boolean isVpcVirtualRouterGateway(VpcOffering vpcOffering); + boolean isValidGateway(VpcOffering vpcOffering); BgpPeer createBgpPeer(CreateBgpPeerCmd createBgpPeerCmd); diff --git a/api/src/main/java/org/apache/cloudstack/query/QueryService.java b/api/src/main/java/org/apache/cloudstack/query/QueryService.java index 88081494320..6a20c2fa248 100644 --- a/api/src/main/java/org/apache/cloudstack/query/QueryService.java +++ b/api/src/main/java/org/apache/cloudstack/query/QueryService.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.query; +import java.util.Arrays; import java.util.List; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -31,6 +32,7 @@ import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; import org.apache.cloudstack.api.command.admin.storage.ListObjectStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListSecondaryStagingStoresCmd; +import org.apache.cloudstack.api.command.admin.storage.ListStorageAccessGroupsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStorageTagsCmd; import org.apache.cloudstack.api.command.admin.storage.heuristics.ListSecondaryStorageSelectorsCmd; @@ -86,6 +88,7 @@ import org.apache.cloudstack.api.response.SecondaryStorageHeuristicsResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.SnapshotResponse; +import org.apache.cloudstack.api.response.StorageAccessGroupResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.StorageTagResponse; import org.apache.cloudstack.api.response.TemplateResponse; @@ -97,6 +100,7 @@ import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.exception.PermissionDeniedException; +import com.cloud.vm.VmDetailConstants; /** * Service used for list api query. @@ -104,15 +108,17 @@ import com.cloud.exception.PermissionDeniedException; */ public interface QueryService { + List RootAdminOnlyVmSettings = Arrays.asList(VmDetailConstants.GUEST_CPU_MODE, VmDetailConstants.GUEST_CPU_MODEL); + // Config keys ConfigKey AllowUserViewDestroyedVM = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.destroyed.vm", "false", "Determines whether users can view their destroyed or expunging vm ", true, ConfigKey.Scope.Account); - static final ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, + ConfigKey UserVMDeniedDetails = new ConfigKey<>(String.class, "user.vm.denied.details", "Advanced", "rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag", "Determines whether users can view certain VM settings. When set to empty, default value used is: rootdisksize, cpuOvercommitRatio, memoryOvercommitRatio, Message.ReservedCapacityFreed.Flag.", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); - static final ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, + ConfigKey UserVMReadOnlyDetails = new ConfigKey<>(String.class, "user.vm.readonly.details", "Advanced", "dataDiskController, rootDiskController", "List of read-only VM settings/details as comma separated string", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); @@ -121,16 +127,20 @@ public interface QueryService { "network offering, zones), we use the flag to determine if the entities should be sorted ascending (when flag is true) " + "or descending (when flag is false). Within the scope of the config all users see the same result.", true, ConfigKey.Scope.Global); - public static final ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, + ConfigKey AllowUserViewAllDomainAccounts = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.all.domain.accounts", "false", "Determines whether users can view all user accounts within the same domain", true, ConfigKey.Scope.Domain); - static final ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", + ConfigKey AllowUserViewAllDataCenters = new ConfigKey<>("Advanced", Boolean.class, "allow.user.view.all.zones", "true", + "Determines whether for instance a Resource Admin can view zones that are not dedicated to them.", true, ConfigKey.Scope.Domain); + + ConfigKey SharePublicTemplatesWithOtherDomains = new ConfigKey<>("Advanced", Boolean.class, "share.public.templates.with.other.domains", "true", "If false, templates of this domain will not show up in the list templates of other domains.", true, ConfigKey.Scope.Domain); ConfigKey ReturnVmStatsOnVmList = new ConfigKey<>("Advanced", Boolean.class, "list.vm.default.details.stats", "true", "Determines whether VM stats should be returned when details are not explicitly specified in listVirtualMachines API request. When false, details default to [group, nics, secgrp, tmpl, servoff, diskoff, backoff, iso, volume, min, affgrp]. When true, all details are returned including 'stats'.", true, ConfigKey.Scope.Global); + ListResponse searchForUsers(ResponseObject.ResponseView responseView, ListUsersCmd cmd) throws PermissionDeniedException; ListResponse searchForUsers(Long domainId, boolean recursive) throws PermissionDeniedException; @@ -193,6 +203,8 @@ public interface QueryService { ListResponse searchForStorageTags(ListStorageTagsCmd cmd); + ListResponse searchForStorageAccessGroups(ListStorageAccessGroupsCmd cmd); + ListResponse searchForHostTags(ListHostTagsCmd cmd); ListResponse listManagementServers(ListMgmtsCmd cmd); diff --git a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java index 0802098cb4f..bba97dff71c 100644 --- a/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java +++ b/api/src/main/java/org/apache/cloudstack/vm/UnmanagedInstanceTO.java @@ -33,6 +33,8 @@ public class UnmanagedInstanceTO { private String internalCSName; + private String path; + private PowerState powerState; private PowerState cloneSourcePowerState; @@ -59,6 +61,9 @@ public class UnmanagedInstanceTO { private String vncPassword; + private String bootType; + private String bootMode; + public String getName() { return name; } @@ -75,6 +80,14 @@ public class UnmanagedInstanceTO { this.internalCSName = internalCSName; } + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + public PowerState getPowerState() { return powerState; } @@ -186,6 +199,22 @@ public class UnmanagedInstanceTO { this, "name", "internalCSName", "hostName", "clusterName")); } + public String getBootType() { + return bootType; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } + + public String getBootMode() { + return bootMode; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + public static class Disk { private String diskId; diff --git a/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java b/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java new file mode 100644 index 00000000000..b6702695305 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/vm/lease/VMLeaseManager.java @@ -0,0 +1,61 @@ +/* + * 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.vm.lease; + +import com.cloud.utils.component.Manager; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.List; + +public interface VMLeaseManager extends Manager { + + int MAX_LEASE_DURATION_DAYS = 365_00; // 100 years + + enum ExpiryAction { + STOP, + DESTROY + } + + enum LeaseActionExecution { + PENDING, + DISABLED, + DONE, + CANCELLED + } + + ConfigKey InstanceLeaseEnabled = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Boolean.class, + "instance.lease.enabled", "false", "Indicates whether to enable the Instance lease," + + " will be applicable only on instances created after lease is enabled. Disabling the feature cancels lease on existing instances with lease." + + " Re-enabling feature will not cause lease expiry actions on grandfathered instances", + true, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.scheduler.interval", "3600", "VM Lease Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventSchedulerInterval = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.eventscheduler.interval", "86400", "Lease expiry event Scheduler interval in seconds", + false, List.of(ConfigKey.Scope.Global)); + + ConfigKey InstanceLeaseExpiryEventDaysBefore = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, Integer.class, + "instance.lease.expiryevent.daysbefore", "7", "Indicates how many days in advance, expiry events will be created before expiry.", + true, List.of(ConfigKey.Scope.Global)); + + void onLeaseFeatureToggle(); +} diff --git a/api/src/test/java/com/cloud/cpu/CPUTest.java b/api/src/test/java/com/cloud/cpu/CPUTest.java new file mode 100644 index 00000000000..dfedf21864c --- /dev/null +++ b/api/src/test/java/com/cloud/cpu/CPUTest.java @@ -0,0 +1,67 @@ +// 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.cpu; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class CPUTest { + @Test + public void testCPUArchGetType() { + assertEquals("i686", CPU.CPUArch.x86.getType()); + assertEquals("x86_64", CPU.CPUArch.amd64.getType()); + assertEquals("aarch64", CPU.CPUArch.arm64.getType()); + } + + @Test + public void testCPUArchGetBits() { + assertEquals(32, CPU.CPUArch.x86.getBits()); + assertEquals(64, CPU.CPUArch.amd64.getBits()); + assertEquals(64, CPU.CPUArch.arm64.getBits()); + } + + @Test + public void testCPUArchFromTypeWithValidValues() { + assertEquals(CPU.CPUArch.x86, CPU.CPUArch.fromType("i686")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("x86_64")); + assertEquals(CPU.CPUArch.arm64, CPU.CPUArch.fromType("aarch64")); + } + + @Test + public void testCPUArchFromTypeWithDefaultForBlankOrNull() { + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType("")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(" ")); + assertEquals(CPU.CPUArch.amd64, CPU.CPUArch.fromType(null)); + } + + @Test + public void testCPUArchFromTypeWithInvalidValue() { + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + CPU.CPUArch.fromType("unsupported"); + }); + assertTrue(exception.getMessage().contains("Unsupported arch type: unsupported")); + } + + @Test + public void testCPUArchGetTypesAsCSV() { + String expectedCSV = "i686,x86_64,aarch64"; + assertEquals(expectedCSV, CPU.CPUArch.getTypesAsCSV()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java new file mode 100644 index 00000000000..af53a539e67 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/cluster/ListClustersCmdTest.java @@ -0,0 +1,83 @@ +// 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.cluster; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.extension.Extension; +import org.apache.cloudstack.extension.ExtensionHelper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.hypervisor.Hypervisor; + +@RunWith(MockitoJUnitRunner.class) +public class ListClustersCmdTest { + + ExtensionHelper extensionHelper; + ListClustersCmd listClustersCmd = new ListClustersCmd(); + + @Before + public void setUp() { + extensionHelper = mock(ExtensionHelper.class); + listClustersCmd.extensionHelper = extensionHelper; + } + + @Test + public void updateClustersExtensions_emptyList_noAction() { + listClustersCmd.updateClustersExtensions(Collections.emptyList()); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_nullList_noAction() { + listClustersCmd.updateClustersExtensions(null); + // No exception, nothing to verify + } + + @Test + public void updateClustersExtensions_withClusterResponses_setsExtension() { + ClusterResponse cluster1 = mock(ClusterResponse.class); + ClusterResponse cluster2 = mock(ClusterResponse.class); + when(cluster1.getInternalId()).thenReturn(1L); + when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + when(cluster2.getInternalId()).thenReturn(2L); + when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.External.name()); + Extension ext1 = mock(Extension.class); + when(ext1.getUuid()).thenReturn("a"); + Extension ext2 = mock(Extension.class); + when(ext2.getUuid()).thenReturn("b"); + when(extensionHelper.getExtensionIdForCluster(anyLong())).thenAnswer(invocation -> invocation.getArguments()[0]); + when(extensionHelper.getExtension(1L)).thenReturn(ext1); + when(extensionHelper.getExtension(2L)).thenReturn(ext2); + List clusters = Arrays.asList(cluster1, cluster2); + listClustersCmd.updateClustersExtensions(clusters); + verify(cluster1).setExtensionId("a"); + verify(cluster2).setExtensionId("b"); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java new file mode 100644 index 00000000000..be21384109c --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuCardCmdTest.java @@ -0,0 +1,90 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuCardCmdTest { + + @Test + public void getDeviceId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceId()); + String deviceId = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "NVIDIA GeForce GTX 1080"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVendorId()); + String vendorId = "10de"; // NVIDIA vendor ID + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getVideoRam() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuCardCmd cmd = new CreateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java new file mode 100644 index 00000000000..fd5c568d5bc --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateGpuDeviceCmdTest.java @@ -0,0 +1,100 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateGpuDeviceCmdTest { + + @Test + public void getHostId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getBusAddress() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getBusAddress()); + String busAddress = "0000:00:1f.6"; + ReflectionTestUtils.setField(cmd, "busAddress", busAddress); + assertEquals(busAddress, cmd.getBusAddress()); + } + + @Test + public void getGpuCardId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(GpuDevice.DeviceType.PCI, cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals("-1", cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + CreateGpuDeviceCmd cmd = new CreateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java new file mode 100644 index 00000000000..c71286bda65 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/CreateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CreateVgpuProfileCmdTest { + + @Test + public void getName() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getDescription() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getCardId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getMaxVgpuPerPgpu() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // 1920 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // 1080 pixels + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java new file mode 100644 index 00000000000..21df915b420 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuCardCmdTest.java @@ -0,0 +1,45 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuCardCmdTest { + + @Test + public void getId() { + DeleteGpuCardCmd cmd = new DeleteGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + CreateVgpuProfileCmd cmd = new CreateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java new file mode 100644 index 00000000000..02b04dd307a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteGpuDeviceCmdTest { + + @Test + public void getIds() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + DeleteGpuDeviceCmd cmd = new DeleteGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java new file mode 100644 index 00000000000..ecd43810e65 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DeleteVgpuProfileCmdTest.java @@ -0,0 +1,45 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DeleteVgpuProfileCmdTest { + + @Test + public void getId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getEntityOwnerId() { + DeleteVgpuProfileCmd cmd = new DeleteVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java new file mode 100644 index 00000000000..8295e06e0d5 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/DiscoverGpuDevicesCmdTest.java @@ -0,0 +1,38 @@ +/* + * 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.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class DiscoverGpuDevicesCmdTest { + + @Test + public void getId() { + DiscoverGpuDevicesCmd cmd = new DiscoverGpuDevicesCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java new file mode 100644 index 00000000000..200bce76933 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ListGpuDevicesCmdByAdminTest.java @@ -0,0 +1,65 @@ +/* + * 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.gpu; + +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ListGpuDevicesCmdByAdminTest { + + @Test + public void getId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getHostId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getHostId()); + Long hostId = 1L; + ReflectionTestUtils.setField(cmd, "hostId", hostId); + assertEquals(hostId, cmd.getHostId()); + } + + @Test + public void getGpuCardId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + ListGpuDevicesCmdByAdmin cmd = new ListGpuDevicesCmdByAdmin(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java new file mode 100644 index 00000000000..ee862409a93 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/ManageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class ManageGpuDeviceCmdTest { + + @Test + public void getIds() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + ManageGpuDeviceCmd cmd = new ManageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java new file mode 100644 index 00000000000..63700d9e908 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UnmanageGpuDeviceCmdTest.java @@ -0,0 +1,47 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UnmanageGpuDeviceCmdTest { + + @Test + public void getIds() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertNull(cmd.getIds()); + List ids = List.of(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "ids", ids); + assertEquals(ids, cmd.getIds()); + } + + @Test + public void getEntityOwnerId() { + UnmanageGpuDeviceCmd cmd = new UnmanageGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java new file mode 100644 index 00000000000..ead7ab9d3d3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuCardCmdTest.java @@ -0,0 +1,72 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuCardCmdTest { + + @Test + public void getId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getDeviceName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getDeviceName()); + String deviceName = "GPU-1234"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getName()); + String name = "Test GPU Card"; + ReflectionTestUtils.setField(cmd, "name", name); + assertEquals(name, cmd.getName()); + } + + @Test + public void getVendorName() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertNull(cmd.getVendorName()); + String vendorName = "NVIDIA"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuCardCmd cmd = new UpdateGpuCardCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java new file mode 100644 index 00000000000..6ebec48aa00 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateGpuDeviceCmdTest.java @@ -0,0 +1,91 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.apache.cloudstack.gpu.GpuDevice; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateGpuDeviceCmdTest { + + @Test + public void getId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getGpuCardId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getGpuCardId()); + Long gpuCardId = 1L; + ReflectionTestUtils.setField(cmd, "gpuCardId", gpuCardId); + assertEquals(gpuCardId, cmd.getGpuCardId()); + } + + @Test + public void getVgpuProfileId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getVgpuProfileId()); + Long vgpuProfileId = 1L; + ReflectionTestUtils.setField(cmd, "vgpuProfileId", vgpuProfileId); + assertEquals(vgpuProfileId, cmd.getVgpuProfileId()); + } + + @Test + public void getType() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getType()); + String type = "MDEV"; + ReflectionTestUtils.setField(cmd, "type", type); + assertEquals(GpuDevice.DeviceType.MDEV, cmd.getType()); + } + + @Test + public void getParentGpuDeviceId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getParentGpuDeviceId()); + Long parentGpuDeviceId = 1L; + ReflectionTestUtils.setField(cmd, "parentGpuDeviceId", parentGpuDeviceId); + assertEquals(parentGpuDeviceId, cmd.getParentGpuDeviceId()); + } + + @Test + public void getNumaNode() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertNull(cmd.getNumaNode()); + String numaNode = "0"; + ReflectionTestUtils.setField(cmd, "numaNode", numaNode); + assertEquals(numaNode, cmd.getNumaNode()); + } + + @Test + public void getEntityOwnerId() { + UpdateGpuDeviceCmd cmd = new UpdateGpuDeviceCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java new file mode 100644 index 00000000000..95acd71096e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/gpu/UpdateVgpuProfileCmdTest.java @@ -0,0 +1,108 @@ +/* + * 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.gpu; + +import com.cloud.user.Account; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class UpdateVgpuProfileCmdTest { + + @Test + public void getId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + assertEquals(id, cmd.getId()); + } + + @Test + public void getProfileName() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getProfileName()); + String profileName = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "profileName", profileName); + assertEquals(profileName, cmd.getProfileName()); + } + + @Test + public void getDescription() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getDescription()); + String description = "Test VGPU Profile Description"; + ReflectionTestUtils.setField(cmd, "description", description); + assertEquals(description, cmd.getDescription()); + } + + @Test + public void getMaxVgpuPerPgpu() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxVgpuPerPgpu()); + Long maxVgpuPerPgpu = 8L; + ReflectionTestUtils.setField(cmd, "maxVgpuPerPgpu", maxVgpuPerPgpu); + assertEquals(maxVgpuPerPgpu, cmd.getMaxVgpuPerPgpu()); + } + + @Test + public void getVideoRam() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getVideoRam()); + Long videoRam = 8192L; // 8 GB + ReflectionTestUtils.setField(cmd, "videoRam", videoRam); + assertEquals(videoRam, cmd.getVideoRam()); + } + + @Test + public void getMaxHeads() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxHeads()); + Long maxHeads = 2L; + ReflectionTestUtils.setField(cmd, "maxHeads", maxHeads); + assertEquals(maxHeads, cmd.getMaxHeads()); + } + + @Test + public void getMaxResolutionX() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionX()); + Long maxResolutionX = 1920L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionX", maxResolutionX); + assertEquals(maxResolutionX, cmd.getMaxResolutionX()); + } + + @Test + public void getMaxResolutionY() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertNull(cmd.getMaxResolutionY()); + Long maxResolutionY = 1080L; // Example resolution + ReflectionTestUtils.setField(cmd, "maxResolutionY", maxResolutionY); + assertEquals(maxResolutionY, cmd.getMaxResolutionY()); + } + + @Test + public void getEntityOwnerId() { + UpdateVgpuProfileCmd cmd = new UpdateVgpuProfileCmd(); + assertEquals(Account.ACCOUNT_ID_SYSTEM, cmd.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java new file mode 100644 index 00000000000..32230056b6a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCategoryCmdTest.java @@ -0,0 +1,46 @@ +// 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.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class AddGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + AddGuestOsCategoryCmd cmd = new AddGuestOsCategoryCmd(); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java new file mode 100644 index 00000000000..3f6a943b3fe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCategoryCmdTest.java @@ -0,0 +1,55 @@ +// 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.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCategoryCmdTest { + + @Test + public void testGetArch() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getName()); + String name = "Name"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void testIsFeatured() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testGetSortKey() { + UpdateGuestOsCategoryCmd cmd = new UpdateGuestOsCategoryCmd(); + Assert.assertNull(cmd.getSortKey()); + Integer sortKey = 100; + ReflectionTestUtils.setField(cmd, "sortKey", sortKey); + Assert.assertEquals(sortKey, cmd.getSortKey()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java new file mode 100644 index 00000000000..f03a791776e --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmdTest.java @@ -0,0 +1,37 @@ +// 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.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +@RunWith(MockitoJUnitRunner.class) +public class UpdateGuestOsCmdTest { + + + @Test + public void testGetZoneId() { + UpdateGuestOsCmd cmd = new UpdateGuestOsCmd(); + Assert.assertNull(cmd.getOsCategoryId()); + Long osCategoryId = 100L; + ReflectionTestUtils.setField(cmd, "osCategoryId", osCategoryId); + Assert.assertEquals(osCategoryId, cmd.getOsCategoryId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java index 6daa5de07cb..bc7f65b0756 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmdTest.java @@ -17,6 +17,8 @@ package org.apache.cloudstack.api.command.admin.offering; +import com.cloud.exception.InvalidParameterValueException; +import org.apache.cloudstack.vm.lease.VMLeaseManager; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -55,4 +57,44 @@ public class CreateServiceOfferingCmdTest { ReflectionTestUtils.setField(createServiceOfferingCmd, "purgeResources", true); Assert.assertTrue(createServiceOfferingCmd.isPurgeResources()); } + + @Test + public void testGetLeaseDuration() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseDuration", 10); + Assert.assertEquals(10, createServiceOfferingCmd.getLeaseDuration().longValue()); + } + + @Test + public void testGetLeaseExpiryAction() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "stop"); + Assert.assertEquals(VMLeaseManager.ExpiryAction.STOP, createServiceOfferingCmd.getLeaseExpiryAction()); + + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "DESTROY"); + Assert.assertEquals(VMLeaseManager.ExpiryAction.DESTROY, createServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "leaseExpiryAction", "Unknown"); + Assert.assertEquals(null, createServiceOfferingCmd.getLeaseExpiryAction()); + } + + @Test + public void testGetVgpuProfileId() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "vgpuProfileId", 10L); + Assert.assertEquals(10L, createServiceOfferingCmd.getVgpuProfileId().longValue()); + } + + @Test + public void testGetGpuCount() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "gpuCount", 2); + Assert.assertEquals(2, createServiceOfferingCmd.getGpuCount().intValue()); + } + + @Test + public void testGetGpuDisplay() { + ReflectionTestUtils.setField(createServiceOfferingCmd, "gpuDisplay", true); + Assert.assertTrue(createServiceOfferingCmd.getGpuDisplay()); + } + } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java index a628f13275c..c8ca1c2afe3 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/resource/PurgeExpungedResourcesCmdTest.java @@ -44,7 +44,7 @@ public class PurgeExpungedResourcesCmdTest { @Spy @InjectMocks - PurgeExpungedResourcesCmd spyCmd = Mockito.spy(new PurgeExpungedResourcesCmd()); + PurgeExpungedResourcesCmd spyCmd; @Test public void testGetResourceType() { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java index a69a7a858ce..c7aeb8ba99b 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/AddObjectStoragePoolCmdTest.java @@ -61,6 +61,8 @@ public class AddObjectStoragePoolCmdTest { String provider = "Simulator"; + Long size = 10L; + Map details; private AutoCloseable closeable; @@ -74,6 +76,7 @@ public class AddObjectStoragePoolCmdTest { ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "url", url); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "providerName", provider); ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "details", details); + ReflectionTestUtils.setField(addObjectStoragePoolCmdSpy, "size", size); addObjectStoragePoolCmdSpy._storageService = storageService; addObjectStoragePoolCmdSpy._responseGenerator = responseGenerator; } @@ -87,12 +90,12 @@ public class AddObjectStoragePoolCmdTest { @Test public void testAddObjectStore() throws DiscoveryException { Mockito.doReturn(objectStore).when(storageService).discoverObjectStore(Mockito.anyString(), - Mockito.anyString(), Mockito.anyString(), any()); + Mockito.anyString(), Mockito.anyLong(), Mockito.anyString(), any()); ObjectStoreResponse objectStoreResponse = new ObjectStoreResponse(); Mockito.doReturn(objectStoreResponse).when(responseGenerator).createObjectStoreResponse(any()); addObjectStoragePoolCmdSpy.execute(); Mockito.verify(storageService, Mockito.times(1)) - .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); + .discoverObjectStore(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java index 98435bf6e38..ad95ce10bd6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/storage/DownloadImageStoreObjectCmdTest.java @@ -20,14 +20,11 @@ package org.apache.cloudstack.api.command.admin.storage; import com.cloud.utils.Pair; import org.apache.cloudstack.api.response.ExtractResponse; import org.apache.cloudstack.storage.browser.StorageBrowser; -import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; @@ -48,18 +45,6 @@ public class DownloadImageStoreObjectCmdTest { @Spy private DownloadImageStoreObjectCmd cmd; - private AutoCloseable closeable; - - @Before - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - } - - @After - public void tearDown() throws Exception { - closeable.close(); - } - @Test public void testExecute() throws Exception { ReflectionTestUtils.setField(cmd, "storeId", 1L); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java index 61a3c8fb9e6..4fec90217fd 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java @@ -68,6 +68,12 @@ public class MigrateVirtualMachineWithVolumeCmdTest { @Mock Host hostMock; + @Mock + private Object job; + + @Mock + private Object _responseObject; + @Spy @InjectMocks MigrateVirtualMachineWithVolumeCmd cmdSpy; diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java index c4e21bb948b..aaa65e10ff4 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCCmdByAdminTest.java @@ -20,7 +20,6 @@ package org.apache.cloudstack.api.command.admin.vpc; import com.cloud.network.vpc.VpcService; import com.cloud.user.AccountService; import com.cloud.utils.db.EntityManager; -import junit.framework.TestCase; import org.apache.cloudstack.api.ResponseGenerator; import org.junit.Assert; import org.junit.Test; @@ -34,7 +33,7 @@ import org.springframework.test.util.ReflectionTestUtils; import java.util.List; @RunWith(MockitoJUnitRunner.class) -public class CreateVPCCmdByAdminTest extends TestCase { +public class CreateVPCCmdByAdminTest { @Mock public VpcService _vpcService; @@ -43,8 +42,10 @@ public class CreateVPCCmdByAdminTest extends TestCase { @Mock public AccountService _accountService; private ResponseGenerator responseGenerator; + @Mock + public Object job; @InjectMocks - CreateVPCCmdByAdmin cmd = new CreateVPCCmdByAdmin(); + CreateVPCCmdByAdmin cmd; @Test public void testBgpPeerIds() { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateSnapshotCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateSnapshotCmdTest.java index 34baebe5257..5fa46ec97e5 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/test/CreateSnapshotCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/test/CreateSnapshotCmdTest.java @@ -93,7 +93,7 @@ public class CreateSnapshotCmdTest extends TestCase { Snapshot snapshot = Mockito.mock(Snapshot.class); try { Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), isNull(), - nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class))).thenReturn(snapshot); + nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), nullable(Map.class), nullable(List.class), nullable(List.class), Mockito.anyBoolean())).thenReturn(snapshot); } catch (Exception e) { Assert.fail("Received exception when success expected " + e.getMessage()); @@ -126,7 +126,7 @@ public class CreateSnapshotCmdTest extends TestCase { try { Mockito.when(volumeApiService.takeSnapshot(nullable(Long.class), nullable(Long.class), nullable(Long.class), - nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList())).thenReturn(null); + nullable(Account.class), nullable(Boolean.class), nullable(Snapshot.LocationType.class), nullable(Boolean.class), any(), Mockito.anyList(), Mockito.anyList(), Mockito.anyBoolean())).thenReturn(null); } catch (Exception e) { Assert.fail("Received exception when success expected " + e.getMessage()); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java new file mode 100644 index 00000000000..47bef14bb61 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/consoleproxy/ListConsoleSessionsCmdTest.java @@ -0,0 +1,124 @@ +// 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 org.apache.cloudstack.context.CallContext; +package org.apache.cloudstack.api.command.user.consoleproxy; + +import org.apache.cloudstack.consoleproxy.ConsoleSession; +import com.cloud.user.AccountService; + +import com.cloud.user.UserAccount; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.consoleproxy.ConsoleAccessManager; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ListConsoleSessionsCmdTest { + @Mock + private AccountService accountServiceMock; + + @Mock + private ConsoleAccessManager consoleAccessManagerMock; + + @Spy + @InjectMocks + private ListConsoleSessionsCmd listConsoleSessionsCmdSpy; + + @Test + public void executeTestApiExecutionShouldCallServiceLayer() { + Mockito.when(consoleAccessManagerMock.listConsoleSessions(listConsoleSessionsCmdSpy)).thenReturn(new ListResponse<>()); + listConsoleSessionsCmdSpy.execute(); + Mockito.verify(consoleAccessManagerMock).listConsoleSessions(listConsoleSessionsCmdSpy); + } + + @Test + public void getEntityOwnerIdTestReturnConsoleSessionIdIfProvided() { + ConsoleSession consoleSessionMock = Mockito.mock(ConsoleSession.class); + long consoleSessionId = 2L; + long accountId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(consoleSessionId); + Mockito.when(consoleAccessManagerMock.listConsoleSessionById(consoleSessionId)).thenReturn(consoleSessionMock); + Mockito.when(consoleSessionMock.getAccountId()).thenReturn(accountId); + + Assert.assertEquals(accountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnAccountIdWhenNoConsoleSessionIdIsProvided() { + long accountId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(accountId); + + Assert.assertEquals(accountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnUserIdWhenNoConsoleSessionIdAndAccountIdAreProvided() { + UserAccount userAccountMock = Mockito.mock(UserAccount.class); + long userId = 2L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(userId); + Mockito.when(accountServiceMock.getUserAccountById(userId)).thenReturn(userAccountMock); + Mockito.when(userAccountMock.getAccountId()).thenReturn(userId); + + Assert.assertEquals(userId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenNoConsoleSessionIdAndAccountIdAndUserIdAreProvided() { + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getAccountId()).thenReturn(null); + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenConsoleSessionDoesNotExist() { + long consoleSessionId = 2L; + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getId()).thenReturn(consoleSessionId); + Mockito.when(consoleAccessManagerMock.listConsoleSessionById(consoleSessionId)).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } + + @Test + public void getEntityOwnerIdTestReturnSystemAccountIdWhenUserAccountDoesNotExist() { + long userId = 2L; + long systemAccountId = 1L; + + Mockito.when(listConsoleSessionsCmdSpy.getUserId()).thenReturn(userId); + Mockito.when(accountServiceMock.getUserAccountById(userId)).thenReturn(null); + + Assert.assertEquals(systemAccountId, listConsoleSessionsCmdSpy.getEntityOwnerId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java new file mode 100644 index 00000000000..54e726eadbe --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuCardsCmdTest.java @@ -0,0 +1,82 @@ +/* + * 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.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + + +public class ListGpuCardsCmdTest { + + @Test + public void getId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getVendorName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorName()); + String vendorName = "vendor name"; + ReflectionTestUtils.setField(cmd, "vendorName", vendorName); + Assert.assertEquals(vendorName, cmd.getVendorName()); + } + + @Test + public void getVendorId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getVendorId()); + String vendorId = "vendor id"; + ReflectionTestUtils.setField(cmd, "vendorId", vendorId); + Assert.assertEquals(vendorId, cmd.getVendorId()); + } + + @Test + public void getDeviceId() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceId()); + String deviceId = "device id"; + ReflectionTestUtils.setField(cmd, "deviceId", deviceId); + Assert.assertEquals(deviceId, cmd.getDeviceId()); + } + + @Test + public void getDeviceName() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertNull(cmd.getDeviceName()); + String deviceName = "device name"; + ReflectionTestUtils.setField(cmd, "deviceName", deviceName); + Assert.assertEquals(deviceName, cmd.getDeviceName()); + } + + @Test + public void getActiveOnly() { + ListGpuCardsCmd cmd = new ListGpuCardsCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java new file mode 100644 index 00000000000..e1a65ee0ece --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListGpuDevicesCmdTest.java @@ -0,0 +1,36 @@ +/* + * 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.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListGpuDevicesCmdTest { + + @Test + public void getVmId() { + ListGpuDevicesCmd cmd = new ListGpuDevicesCmd(); + Assert.assertNull(cmd.getVmId()); + Long vmId = 1L; + ReflectionTestUtils.setField(cmd, "vmId", vmId); + Assert.assertEquals(vmId, cmd.getVmId()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java new file mode 100644 index 00000000000..7616abd1f8d --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/gpu/ListVgpuProfilesCmdTest.java @@ -0,0 +1,63 @@ +/* + * 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.user.gpu; + +import org.junit.Assert; +import org.junit.Test; +import org.springframework.test.util.ReflectionTestUtils; + +public class ListVgpuProfilesCmdTest { + + @Test + public void getId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getId()); + Long id = 1L; + ReflectionTestUtils.setField(cmd, "id", id); + Assert.assertEquals(id, cmd.getId()); + } + + @Test + public void getName() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getName()); + String name = "Test VGPU Profile"; + ReflectionTestUtils.setField(cmd, "name", name); + Assert.assertEquals(name, cmd.getName()); + } + + @Test + public void getCardId() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertNull(cmd.getCardId()); + Long cardId = 1L; + ReflectionTestUtils.setField(cmd, "cardId", cardId); + Assert.assertEquals(cardId, cmd.getCardId()); + } + + @Test + public void getActiveOnly() { + ListVgpuProfilesCmd cmd = new ListVgpuProfilesCmd(); + Assert.assertFalse(cmd.getActiveOnly()); + Boolean activeOnly = true; + ReflectionTestUtils.setField(cmd, "activeOnly", activeOnly); + Assert.assertEquals(activeOnly, cmd.getActiveOnly()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java new file mode 100644 index 00000000000..f417dc5f876 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/guest/ListGuestOsCategoriesCmdTest.java @@ -0,0 +1,87 @@ +// 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.user.guest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.cpu.CPU; + +@RunWith(MockitoJUnitRunner.class) +public class ListGuestOsCategoriesCmdTest { + + @Test + public void testIsFeatured() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", false); + Assert.assertFalse(cmd.isFeatured()); + ReflectionTestUtils.setField(cmd, "featured", true); + Assert.assertTrue(cmd.isFeatured()); + } + + @Test + public void testIsIso() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", false); + Assert.assertFalse(cmd.isIso()); + ReflectionTestUtils.setField(cmd, "iso", true); + Assert.assertTrue(cmd.isIso()); + } + + @Test + public void testIsVnf() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", false); + Assert.assertFalse(cmd.isVnf()); + ReflectionTestUtils.setField(cmd, "vnf", true); + Assert.assertTrue(cmd.isVnf()); + } + + @Test + public void testGetZoneId() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getZoneId()); + Long zoneId = 100L; + ReflectionTestUtils.setField(cmd, "zoneId", zoneId); + Assert.assertEquals(zoneId, cmd.getZoneId()); + } + + @Test + public void testGetArch() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertNull(cmd.getArch()); + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + ReflectionTestUtils.setField(cmd, "arch", arch.getType()); + Assert.assertEquals(arch, cmd.getArch()); + } + + @Test + public void testIsShowIcon() { + ListGuestOsCategoriesCmd cmd = new ListGuestOsCategoriesCmd(); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", false); + Assert.assertFalse(cmd.isShowIcon()); + ReflectionTestUtils.setField(cmd, "showIcon", true); + Assert.assertTrue(cmd.isShowIcon()); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java index 415ee01ba16..c42a7882c54 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmdTest.java @@ -41,7 +41,10 @@ public class UpdateNetworkCmdTest { NetworkService networkService; @Mock public EntityManager _entityMgr; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; @InjectMocks UpdateNetworkCmd cmd = new UpdateNetworkCmd(); @@ -176,15 +179,13 @@ public class UpdateNetworkCmdTest { ReflectionTestUtils.setField(cmd, "id", networkId); ReflectionTestUtils.setField(cmd, "publicMtu", publicmtu); Network network = Mockito.mock(Network.class); - responseGenerator = Mockito.mock(ResponseGenerator.class); NetworkResponse response = Mockito.mock(NetworkResponse.class); response.setPublicMtu(publicmtu); Mockito.when(networkService.getNetwork(networkId)).thenReturn(network); Mockito.when(networkService.updateGuestNetwork(cmd)).thenReturn(network); - cmd._responseGenerator = responseGenerator; - Mockito.when(responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); + Mockito.when(_responseGenerator.createNetworkResponse(ResponseObject.ResponseView.Restricted, network)).thenReturn(response); cmd.execute(); - Mockito.verify(responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); + Mockito.verify(_responseGenerator).createNetworkResponse(Mockito.any(ResponseObject.ResponseView.class), Mockito.any(Network.class)); NetworkResponse actualResponse = (NetworkResponse) cmd.getResponseObject(); Assert.assertEquals(response, actualResponse); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java index 632496ad215..db27cc76ec9 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/snapshot/CopySnapshotCmdTest.java @@ -87,7 +87,12 @@ public class CopySnapshotCmdTest { @Test (expected = ServerApiException.class) public void testExecuteWrongNoParams() { + UUIDManager uuidManager = Mockito.mock(UUIDManager.class); + SnapshotApiService snapshotApiService = Mockito.mock(SnapshotApiService.class); final CopySnapshotCmd cmd = new CopySnapshotCmd(); + cmd._uuidMgr = uuidManager; + cmd._snapshotService = snapshotApiService; + try { cmd.execute(); } catch (ResourceUnavailableException e) { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java index 3f47a078445..8b7db292462 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/userdata/ListUserDataCmdTest.java @@ -68,7 +68,7 @@ public class ListUserDataCmdTest { Pair, Integer> result = new Pair, Integer>(userDataList, 1); UserDataResponse userDataResponse = Mockito.mock(UserDataResponse.class); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); Mockito.when(_responseGenerator.createUserDataResponse(userData)).thenReturn(userDataResponse); cmd.execute(); @@ -82,7 +82,7 @@ public class ListUserDataCmdTest { List userDataList = new ArrayList(); Pair, Integer> result = new Pair, Integer>(userDataList, 0); - Mockito.when(_mgr.listUserDatas(cmd)).thenReturn(result); + Mockito.when(_mgr.listUserDatas(cmd, false)).thenReturn(result); cmd.execute(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java new file mode 100644 index 00000000000..f7e3e38d9c3 --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmdTest.java @@ -0,0 +1,483 @@ +// 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.user.vm; + +import static org.junit.Assert.assertEquals; +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.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.BootMode; +import org.apache.cloudstack.api.ApiConstants.BootType; +import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; +import org.apache.cloudstack.vm.lease.VMLeaseManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkService; +import com.cloud.utils.db.EntityManager; +import com.cloud.vm.VmDetailConstants; +import com.cloud.network.Network; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.offering.DiskOffering; +import com.cloud.network.Network.IpAddresses; +import com.cloud.vm.VmDiskInfo; + +@RunWith(MockitoJUnitRunner.class) +public class DeployVMCmdTest { + + @Spy + private DeployVMCmd cmd = new DeployVMCmd(); + + @Test + public void testGetBootType_ValidUEFI() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.UEFI, result); + } + + @Test + public void testGetBootTypeValidBIOS() { + ReflectionTestUtils.setField(cmd, "bootType", "BIOS"); + + BootType result = cmd.getBootType(); + + assertEquals(BootType.BIOS, result); + } + + @Test + public void testGetBootTypeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootType", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootType(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootType INVALID")); + } + + @Test + public void testGetBootTypeNullValue() { + ReflectionTestUtils.setField(cmd, "bootType", null); + + BootType result = cmd.getBootType(); + + assertNull(result); + } + + @Test + public void testGetBootModeValidSecure() { + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.SECURE, result); + } + + @Test + public void testGetBootModeValidLegacy() { + ReflectionTestUtils.setField(cmd, "bootMode", "LEGACY"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + BootMode result = cmd.getBootMode(); + + assertEquals(BootMode.LEGACY, result); + } + + @Test + public void testGetBootModeInvalidValue() { + ReflectionTestUtils.setField(cmd, "bootMode", "INVALID"); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("Invalid bootmode: INVALID specified for VM: null. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetBootModeUEFIWithoutBootMode() { + ReflectionTestUtils.setField(cmd, "bootMode", null); + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getBootMode(); + }); + assertTrue(thrownException.getMessage().contains("bootmode must be specified for the VM with boot type: UEFI. Valid values are: [LEGACY, SECURE]")); + } + + @Test + public void testGetDetails() { + ReflectionTestUtils.setField(cmd, "bootType", "UEFI"); + ReflectionTestUtils.setField(cmd, "bootMode", "SECURE"); + ReflectionTestUtils.setField(cmd, "rootdisksize", 100L); + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + ReflectionTestUtils.setField(cmd, "iothreadsEnabled", true); + ReflectionTestUtils.setField(cmd, "nicMultiqueueNumber", null); + ReflectionTestUtils.setField(cmd, "nicPackedVirtQueues", null); + ReflectionTestUtils.setField(cmd, "details", new HashMap<>()); + + Map result = cmd.getDetails(); + + assertEquals("SECURE", result.get("UEFI")); + assertEquals("100", result.get(VmDetailConstants.ROOT_DISK_SIZE)); + assertEquals("native", result.get(VmDetailConstants.IO_POLICY)); + assertEquals("true", result.get(VmDetailConstants.IOTHREADS)); + } + + @Test + public void testGetLeaseExpiryActionValidStop() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "STOP"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.STOP, result); + } + + @Test + public void testGetLeaseExpiryActionValidDestroy() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "DESTROY"); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertEquals(VMLeaseManager.ExpiryAction.DESTROY, result); + } + + @Test + public void testGetLeaseExpiryActionInvalidValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getLeaseExpiryAction(); + }); + assertTrue(thrownException.getMessage().contains("Invalid value configured for leaseexpiryaction")); + } + + @Test + public void testGetLeaseExpiryActionNullValue() { + ReflectionTestUtils.setField(cmd, "leaseExpiryAction", null); + + VMLeaseManager.ExpiryAction result = cmd.getLeaseExpiryAction(); + + assertNull(result); + } + + @Test + public void testGetIoDriverPolicyValidThrottle() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "native"); + + IoDriverPolicy result = cmd.getIoDriverPolicy(); + + assertEquals(IoDriverPolicy.valueOf("NATIVE"), result); + } + + @Test + public void testGetIoDriverPolicyInvalidValue() { + ReflectionTestUtils.setField(cmd, "ioDriverPolicy", "INVALID"); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIoDriverPolicy(); + }); + assertTrue(thrownException.getMessage().contains("Invalid io policy INVALID")); + } + + @Test + public void testGetNetworkIds() { + List networkIds = Arrays.asList(1L, 2L, 3L); + ReflectionTestUtils.setField(cmd, "networkIds", networkIds); + ReflectionTestUtils.setField(cmd, "vAppNetworks", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + List result = cmd.getNetworkIds(); + + assertEquals(networkIds, result); + } + + @Test + public void testGetNetworkIdsVAppNetworks() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ip6Address", null); + + List result = cmd.getNetworkIds(); + + assertTrue(result.isEmpty()); + } + + @Test + public void testGetNetworkIdsVAppNetworksAndNetworkIds() { + Map vAppNetworks = new HashMap<>(); + vAppNetworks.put("network1", new HashMap()); + ReflectionTestUtils.setField(cmd, "vAppNetworks", vAppNetworks); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("nicnetworklist can't be specified along with networkids")); + } + + @Test + public void testGetNetworkIdsIpToNetworkListAndNetworkIds() { + Map ipToNetworkList = new HashMap<>(); + ipToNetworkList.put("0", new HashMap()); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getNetworkIds(); + }); + assertTrue(thrownException.getMessage().contains("ipToNetworkMap can't be specified along with networkIds or ipAddress")); + } + + @Test + public void testGetIpToNetworkMap_WithNetworkIds() { + ReflectionTestUtils.setField(cmd, "networkIds", Arrays.asList(1L, 2L)); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithIpAddress() { + ReflectionTestUtils.setField(cmd, "ipAddress", "192.168.1.1"); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter")); + } + + @Test + public void testGetIpToNetworkMap_WithEmptyIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", new HashMap<>()); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetIpToNetworkMap_WithNullIpToNetworkList() { + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + ReflectionTestUtils.setField(cmd, "ipToNetworkList", null); + + Map result = cmd.getIpToNetworkMap(); + + assertNull(result); + } + + @Test + public void testGetDataDiskInfoList() { + Map dataDisksDetails = new HashMap<>(); + Map dataDisk = new HashMap<>(); + dataDisk.put(ApiConstants.DISK_OFFERING_ID, "offering-uuid"); + dataDisk.put(ApiConstants.DEVICE_ID, "0"); + dataDisk.put(ApiConstants.MIN_IOPS, "1000"); + dataDisk.put(ApiConstants.MAX_IOPS, "2000"); + dataDisksDetails.put("0", dataDisk); + + ReflectionTestUtils.setField(cmd, "dataDisksDetails", dataDisksDetails); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + DiskOffering diskOffering = mock(DiskOffering.class); + when(diskOffering.getDiskSize()).thenReturn(1024 * 1024 * 1024L); + when(diskOffering.isCustomizedIops()).thenReturn(true); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(diskOffering); + + List result = cmd.getDataDiskInfoList(); + + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(diskOffering, result.get(0).getDiskOffering()); + assertEquals(1L, result.get(0).getSize().longValue()); + assertEquals(1000L, result.get(0).getMinIops().longValue()); + assertEquals(2000L, result.get(0).getMaxIops().longValue()); + } + + @Test + public void testGetIpAddressesFromIpMap() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "00:11:22:33:44:55"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + Map result = cmd.getIpToNetworkMap(); + + assertNotNull(result); + assertTrue(result.containsKey(1L)); + assertEquals(result.get(1L).getIp4Address(), "192.168.1.100"); + assertEquals(result.get(1L).getMacAddress(), "00:11:22:33:44:55"); + } + + @Test + public void testGetIpAddressesFromIpMapInvalidMac() { + Map ipToNetworkList = new HashMap<>(); + Map ipMap = new HashMap<>(); + ipMap.put("ip", "192.168.1.100"); + ipMap.put("mac", "invalid-mac"); + ipMap.put("networkid", "1"); + ipToNetworkList.put("0", ipMap); + + ReflectionTestUtils.setField(cmd, "ipToNetworkList", ipToNetworkList); + ReflectionTestUtils.setField(cmd, "networkIds", null); + ReflectionTestUtils.setField(cmd, "ipAddress", null); + + Network mockNetwork = mock(Network.class); + NetworkService networkServiceMock = mock(NetworkService.class); + ReflectionTestUtils.setField(cmd, "_networkService", networkServiceMock); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getIpToNetworkMap(); + }); + assertTrue(thrownException.getMessage().contains("Mac address is not valid")); + } + + @Test + public void testGetDhcpOptionsMap() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("networkid", "network-1"); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptions.put("dhcp:66", "www.test.com"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + Map> result = cmd.getDhcpOptionsMap(); + + assertNotNull(result); + assertTrue(result.containsKey("network-1")); + Map networkOptions = result.get("network-1"); + assertEquals("url-value", networkOptions.get(114)); + assertEquals("www.test.com", networkOptions.get(66)); + } + + @Test + public void testGetDhcpOptionsMap_WithMissingNetworkId() { + Map dhcpOptionsNetworkList = new HashMap<>(); + Map dhcpOptions = new HashMap<>(); + dhcpOptions.put("dhcp:114", "url-value"); + dhcpOptionsNetworkList.put("0", dhcpOptions); + + ReflectionTestUtils.setField(cmd, "dhcpOptionsNetworkList", dhcpOptionsNetworkList); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, () -> { + cmd.getDhcpOptionsMap(); + }); + assertTrue(thrownException.getMessage().contains("No networkid specified when providing extra dhcp options")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMap() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "template-uuid"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + VirtualMachineTemplate mockTemplate = mock(VirtualMachineTemplate.class); + when(mockTemplate.getId()).thenReturn(1L); + + DiskOffering mockOffering = mock(DiskOffering.class); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "template-uuid")).thenReturn(mockTemplate); + when(entityMgr.findByUuid(DiskOffering.class, "offering-uuid")).thenReturn(mockOffering); + + Map result = cmd.getDataDiskTemplateToDiskOfferingMap(); + + assertNotNull(result); + assertEquals(mockOffering, result.get(1L)); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapWithDiskOfferingId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", 1L); + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", new HashMap<>()); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("diskofferingid parameter can't be specified along with datadisktemplatetodiskofferinglist parameter")); + } + + @Test + public void testGetDataDiskTemplateToDiskOfferingMapInvalidTemplateId() { + ReflectionTestUtils.setField(cmd, "diskOfferingId", null); + + Map dataDiskTemplateToDiskOfferingList = new HashMap<>(); + Map dataDiskTemplate = new HashMap<>(); + dataDiskTemplate.put("datadisktemplateid", "invalid-template"); + dataDiskTemplate.put("diskofferingid", "offering-uuid"); + dataDiskTemplateToDiskOfferingList.put("0", dataDiskTemplate); + + ReflectionTestUtils.setField(cmd, "dataDiskTemplateToDiskOfferingList", dataDiskTemplateToDiskOfferingList); + + EntityManager entityMgr = mock(EntityManager.class); + ReflectionTestUtils.setField(cmd, "_entityMgr", entityMgr); + when(entityMgr.findByUuid(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + when(entityMgr.findById(VirtualMachineTemplate.class, "invalid-template")).thenReturn(null); + + InvalidParameterValueException thrownException = assertThrows(InvalidParameterValueException.class, () -> { + cmd.getDataDiskTemplateToDiskOfferingMap(); + }); + assertTrue(thrownException.getMessage().contains("Unable to translate and find entity with datadisktemplateid")); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java new file mode 100644 index 00000000000..48b41a47fff --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vm/ListVMsCmdTest.java @@ -0,0 +1,223 @@ +// 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.user.vm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.anySet; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.cloud.server.ResourceIcon; +import com.cloud.server.ResourceIconManager; +import com.cloud.server.ResourceTag; +import com.cloud.storage.GuestOS; +import com.cloud.utils.db.EntityManager; + +public class ListVMsCmdTest { + + EntityManager _entityMgr; + ResourceIconManager resourceIconManager; + ResponseGenerator _responseGenerator; + + ListVMsCmd cmd; + + @Before + public void setup() { + _entityMgr = mock(EntityManager.class); + resourceIconManager = mock(ResourceIconManager.class); + _responseGenerator = mock(ResponseGenerator.class); + cmd = spy(ListVMsCmd.class); + cmd._entityMgr = _entityMgr; + cmd.resourceIconManager = resourceIconManager; + cmd._responseGenerator = _responseGenerator; + } + + @Test + public void testUpdateVMResponse_withMixedIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map initialIcons = new HashMap<>(); + initialIcons.put(vm1Uuid, icon1); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.UserVm, Set.of(vm1Uuid, vm2Uuid))) + .thenReturn(initialIcons); + Map fallbackIcons = Map.of(vm2Uuid, icon2); + doReturn(fallbackIcons).when(cmd).getResourceIconsForUsingTemplateIso(anyList()); + ResourceIconResponse iconResponse1 = new ResourceIconResponse(); + ResourceIconResponse iconResponse2 = new ResourceIconResponse(); + when(_responseGenerator.createResourceIconResponse(icon1)).thenReturn(iconResponse1); + when(_responseGenerator.createResourceIconResponse(icon2)).thenReturn(iconResponse2); + cmd.updateVMResponse(responses); + verify(vm1).setResourceIconResponse(iconResponse1); + verify(vm2).setResourceIconResponse(iconResponse2); + } + + @Test + public void testUpdateVMResponse_withEmptyList() { + cmd.updateVMResponse(Collections.emptyList()); + verify(resourceIconManager, never()).getByResourceTypeAndIds(Mockito.any(), Mockito.anyCollection()); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + String vm2Uuid = UUID.randomUUID().toString(); + String iso2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getId()).thenReturn(vm2Uuid); + when(vm2.getTemplateId()).thenReturn(null); + when(vm2.getIsoId()).thenReturn(iso2Uuid); + List responses = Arrays.asList(vm1, vm2); + Map templateIcons = new HashMap<>(); + templateIcons.put(template1Uuid, mock(ResourceIcon.class)); + Map isoIcons = new HashMap<>(); + isoIcons.put(iso2Uuid, mock(ResourceIcon.class)); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.Template, Set.of(template1Uuid))) + .thenReturn(templateIcons); + when(resourceIconManager.getByResourceTypeAndUuids(ResourceTag.ResourceObjectType.ISO, Set.of(iso2Uuid))) + .thenReturn(isoIcons); + doReturn(Collections.emptyMap()).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(2, result.size()); + assertTrue(result.containsKey(vm1Uuid)); + assertTrue(result.containsKey(vm2Uuid)); + assertEquals(templateIcons.get(template1Uuid), result.get(vm1Uuid)); + assertEquals(isoIcons.get(iso2Uuid), result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsForUsingTemplateIso_withMissingIcons() { + String vm1Uuid = UUID.randomUUID().toString(); + String template1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getId()).thenReturn(vm1Uuid); + when(vm1.getTemplateId()).thenReturn(template1Uuid); + when(vm1.getIsoId()).thenReturn(null); + List responses = List.of(vm1); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.Template), anySet())) + .thenReturn(Collections.emptyMap()); + when(resourceIconManager.getByResourceTypeAndUuids(eq(ResourceTag.ResourceObjectType.ISO), anySet())) + .thenReturn(Collections.emptyMap()); + Map fallbackIcons = Map.of(vm1Uuid, mock(ResourceIcon.class)); + doReturn(fallbackIcons).when(cmd).getResourceIconsUsingOsCategory(anyList()); + Map result = cmd.getResourceIconsForUsingTemplateIso(responses); + assertEquals(1, result.size()); + assertEquals(fallbackIcons.get("vm1"), result.get("vm1")); + } + + @Test + public void testGetResourceIconsUsingOsCategory_withValidData() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(os1Uuid); + when(vm1.getId()).thenReturn(vm1Uuid); + String vm2Uuid = UUID.randomUUID().toString(); + String os2Uuid = UUID.randomUUID().toString(); + UserVmResponse vm2 = mock(UserVmResponse.class); + when(vm2.getGuestOsId()).thenReturn(os2Uuid); + when(vm2.getId()).thenReturn(vm2Uuid); + List responses = Arrays.asList(vm1, vm2); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getUuid()).thenReturn(os1Uuid); + when(guestOS1.getCategoryId()).thenReturn(10L); + GuestOS guestOS2 = mock(GuestOS.class); + when(guestOS2.getUuid()).thenReturn(os2Uuid); + when(guestOS2.getCategoryId()).thenReturn(20L); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Arrays.asList(guestOS1, guestOS2)); + ResourceIcon icon1 = mock(ResourceIcon.class); + ResourceIcon icon2 = mock(ResourceIcon.class); + Map categoryIcons = new HashMap<>(); + categoryIcons.put(10L, icon1); + categoryIcons.put(20L, icon2); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(categoryIcons); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertEquals(2, result.size()); + assertEquals(icon1, result.get(vm1Uuid)); + assertEquals(icon2, result.get(vm2Uuid)); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingGuestOS() { + String vm1Uuid = UUID.randomUUID().toString(); + String os1Uuid = UUID.randomUUID().toString(); + UserVmResponse vm1 = mock(UserVmResponse.class); + when(vm1.getGuestOsId()).thenReturn(vm1Uuid); + when(vm1.getId()).thenReturn(os1Uuid); + List responses = Collections.singletonList(vm1); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.emptyList()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.isEmpty()); + } + + @Test + public void testGetResourceIconsUsingOsCategory_missingIcon() { + UserVmResponse vm1 = mock(UserVmResponse.class); + String vmUuid = UUID.randomUUID().toString(); + String osUuid = UUID.randomUUID().toString(); + when(vm1.getGuestOsId()).thenReturn(osUuid); + when(vm1.getId()).thenReturn(vmUuid); + List responses = Collections.singletonList(vm1); + GuestOS guestOS1 = mock(GuestOS.class); + when(guestOS1.getCategoryId()).thenReturn(10L); + when(guestOS1.getUuid()).thenReturn(osUuid); + when(_entityMgr.listByUuids(eq(GuestOS.class), anySet())) + .thenReturn(Collections.singletonList(guestOS1)); + when(resourceIconManager.getByResourceTypeAndIds(eq(ResourceTag.ResourceObjectType.GuestOsCategory), anySet())) + .thenReturn(Collections.emptyMap()); + Map result = cmd.getResourceIconsUsingOsCategory(responses); + assertTrue(result.containsKey(vmUuid)); + assertNull(result.get(vmUuid)); + } +} diff --git a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java index 2505c67e87d..18c03f8f4bb 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmdTest.java @@ -25,7 +25,6 @@ import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcService; import com.cloud.user.AccountService; import com.cloud.utils.db.EntityManager; -import junit.framework.TestCase; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; import org.apache.cloudstack.api.response.VpcResponse; @@ -39,7 +38,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; @RunWith(MockitoJUnitRunner.class) -public class CreateVPCCmdTest extends TestCase { +public class CreateVPCCmdTest { @Mock public VpcService _vpcService; @@ -47,9 +46,13 @@ public class CreateVPCCmdTest extends TestCase { public EntityManager _entityMgr; @Mock public AccountService _accountService; - private ResponseGenerator responseGenerator; + @Mock + private ResponseGenerator _responseGenerator; + @Mock + private Object job; + @InjectMocks - CreateVPCCmd cmd = new CreateVPCCmd(); + CreateVPCCmd cmd; @Test public void testGetAccountName() { @@ -185,11 +188,9 @@ public class CreateVPCCmdTest extends TestCase { VpcResponse response = Mockito.mock(VpcResponse.class); ReflectionTestUtils.setField(cmd, "id", 1L); - responseGenerator = Mockito.mock(ResponseGenerator.class); Mockito.doNothing().when(_vpcService).startVpc(cmd); Mockito.when(_entityMgr.findById(Mockito.eq(Vpc.class), Mockito.any(Long.class))).thenReturn(vpc); - cmd._responseGenerator = responseGenerator; - Mockito.when(responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response); + Mockito.when(_responseGenerator.createVpcResponse(ResponseObject.ResponseView.Restricted, vpc)).thenReturn(response); cmd.execute(); Mockito.verify(_vpcService, Mockito.times(1)).startVpc(cmd); } diff --git a/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java new file mode 100644 index 00000000000..ae4314aa11a --- /dev/null +++ b/api/src/test/java/org/apache/cloudstack/extension/ExtensionCustomActionTest.java @@ -0,0 +1,484 @@ +//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.extension; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.junit.Test; + +import com.cloud.exception.InvalidParameterValueException; + +public class ExtensionCustomActionTest { + + @Test + public void testResourceType() { + ExtensionCustomAction.ResourceType vmType = ExtensionCustomAction.ResourceType.VirtualMachine; + assertEquals(com.cloud.vm.VirtualMachine.class, vmType.getAssociatedClass()); + } + + @Test + public void testParameterTypeSupportsOptions() { + assertTrue(ExtensionCustomAction.Parameter.Type.STRING.canSupportsOptions()); + assertTrue(ExtensionCustomAction.Parameter.Type.NUMBER.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.BOOLEAN.canSupportsOptions()); + assertFalse(ExtensionCustomAction.Parameter.Type.DATE.canSupportsOptions()); + } + + @Test + public void testValidationFormatBaseType() { + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL.getBaseType()); + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL.getBaseType()); + assertNull(ExtensionCustomAction.Parameter.ValidationFormat.NONE.getBaseType()); + } + + @Test + public void testParameterFromMapValid() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.REQUIRED, "true"); + map.put(ApiConstants.VALUE_OPTIONS, "test@example.com,another@test.com"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals("testParam", param.getName()); + assertEquals(ExtensionCustomAction.Parameter.Type.STRING, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, param.getValidationFormat()); + assertTrue(param.isRequired()); + assertEquals(2, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains("test@example.com")); + assertTrue(param.getValueOptions().contains("another@test.com")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapEmptyName() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, ""); + map.put(ApiConstants.TYPE, "STRING"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapNoType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidType() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "INVALID_TYPE"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidValidationFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "INVALID_FORMAT"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapMismatchedTypeAndFormat() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testParameterFromMapWithNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALIDATION_FORMAT, "DECIMAL"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,2.7,3.0"); + + ExtensionCustomAction.Parameter param = ExtensionCustomAction.Parameter.fromMap(map); + + assertEquals(ExtensionCustomAction.Parameter.Type.NUMBER, param.getType()); + assertEquals(ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, param.getValidationFormat()); + assertEquals(3, param.getValueOptions().size()); + assertTrue(param.getValueOptions().contains(1.5f)); + assertTrue(param.getValueOptions().contains(2.7f)); + assertTrue(param.getValueOptions().contains(3.0f)); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidNumberOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "NUMBER"); + map.put(ApiConstants.VALUE_OPTIONS, "1.5,invalid,3.0"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test(expected = InvalidParameterValueException.class) + public void testParameterFromMapInvalidEmailOptions() { + Map map = new HashMap<>(); + map.put(ApiConstants.NAME, "testParam"); + map.put(ApiConstants.TYPE, "STRING"); + map.put(ApiConstants.VALIDATION_FORMAT, "EMAIL"); + map.put(ApiConstants.VALUE_OPTIONS, "valid@email.com,invalid-email"); + + ExtensionCustomAction.Parameter.fromMap(map); + } + + @Test + public void testValidatedValueString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + Object result = param.validatedValue("test@example.com"); + assertEquals("test@example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidEmail() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + false + ); + + param.validatedValue("invalid-email"); + } + + @Test + public void testValidatedValueUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + String validUUID = "550e8400-e29b-41d4-a716-446655440000"; + Object result = param.validatedValue(validUUID); + assertEquals(validUUID, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidUUID() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.UUID, + null, + false + ); + + param.validatedValue("invalid-uuid"); + } + + @Test + public void testValidatedValueURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + Object result = param.validatedValue("https://example.com"); + assertEquals("https://example.com", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidURL() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.URL, + null, + false + ); + + param.validatedValue("not-a-url"); + } + + @Test + public void testValidatedValuePassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + Object result = param.validatedValue("mypassword"); + assertEquals("mypassword", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmptyPassword() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.PASSWORD, + null, + false + ); + + param.validatedValue(" "); + } + + @Test + public void testValidatedValueNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("42"); + assertEquals(42, result); + } + + @Test + public void testValidatedValueDecimal() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, + null, + false + ); + + Object result = param.validatedValue("3.14"); + assertEquals(3.14f, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidNumber() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue("not-a-number"); + } + + @Test + public void testValidatedValueBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("true"); + assertEquals(true, result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueInvalidBoolean() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.BOOLEAN, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + Object result = param.validatedValue("maybe"); + } + + @Test + public void testValidatedValueWithOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + Object result = param.validatedValue("option2"); + assertEquals("option2", result); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueNotInOptions() { + List options = Arrays.asList("option1", "option2", "option3"); + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + options, + false + ); + + param.validatedValue("option4"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidatedValueEmpty() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, + null, + false + ); + + param.validatedValue(""); + } + + @Test + public void testValidateParameterValues() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("required2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true), + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", "value1"); + suppliedValues.put("required2", "42"); + suppliedValues.put("optional", "optionalValue"); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + + assertEquals("value1", result.get("required1")); + assertEquals(42, result.get("required2")); + assertEquals("optionalValue", result.get("optional")); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesMissingRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateParameterValuesEmptyRequired() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("required1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, true) + ); + + Map suppliedValues = new HashMap<>(); + suppliedValues.put("required1", " "); + + ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, suppliedValues); + } + + @Test + public void testValidateParameterValuesNullSupplied() { + List paramDefs = Arrays.asList( + new ExtensionCustomAction.Parameter("optional", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.NONE, null, false) + ); + + Map result = ExtensionCustomAction.Parameter.validateParameterValues(paramDefs, null); + assertTrue(result.isEmpty()); + } + + @Test + public void testJsonSerializationDeserialization() { + List originalParams = Arrays.asList( + new ExtensionCustomAction.Parameter("param1", ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, Arrays.asList("test@example.com"), true), + new ExtensionCustomAction.Parameter("param2", ExtensionCustomAction.Parameter.Type.NUMBER, + ExtensionCustomAction.Parameter.ValidationFormat.DECIMAL, Arrays.asList(1.5f, 2.7f), false) + ); + + String json = ExtensionCustomAction.Parameter.toJsonFromList(originalParams); + List deserializedParams = ExtensionCustomAction.Parameter.toListFromJson(json); + + assertEquals(originalParams.size(), deserializedParams.size()); + assertEquals(originalParams.get(0).getName(), deserializedParams.get(0).getName()); + assertEquals(originalParams.get(0).getType(), deserializedParams.get(0).getType()); + assertEquals(originalParams.get(0).getValidationFormat(), deserializedParams.get(0).getValidationFormat()); + assertEquals(originalParams.get(0).isRequired(), deserializedParams.get(0).isRequired()); + assertEquals(originalParams.get(0).getValueOptions(), deserializedParams.get(0).getValueOptions()); + } + + @Test + public void testToString() { + ExtensionCustomAction.Parameter param = new ExtensionCustomAction.Parameter( + "testParam", + ExtensionCustomAction.Parameter.Type.STRING, + ExtensionCustomAction.Parameter.ValidationFormat.EMAIL, + null, + true + ); + + String result = param.toString(); + assertTrue(result.contains("testParam")); + assertTrue(result.contains("STRING")); + assertTrue(result.contains("true")); + } +} diff --git a/build/replace.properties b/build/replace.properties index ce38727b80a..8c3812eb7d2 100644 --- a/build/replace.properties +++ b/build/replace.properties @@ -28,3 +28,4 @@ MSMNTDIR=/mnt COMPONENTS-SPEC=components.xml REMOTEHOST=localhost COMMONLIBDIR=client/target/common/ +EXTENSIONSDEPLOYMENTMODE=developer diff --git a/client/bindir/cloud-setup-management.in b/client/bindir/cloud-setup-management.in index 70e727b40b2..84c87ae2e44 100755 --- a/client/bindir/cloud-setup-management.in +++ b/client/bindir/cloud-setup-management.in @@ -16,13 +16,27 @@ # 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 + if __name__ == '__main__': initLoging("@MSLOGDIR@/setupManagement.log") glbEnv = globalEnv() diff --git a/client/bindir/cloud-update-xenserver-licenses.in b/client/bindir/cloud-update-xenserver-licenses.in index 2be3a083f97..fca48f9eafb 100755 --- a/client/bindir/cloud-update-xenserver-licenses.in +++ b/client/bindir/cloud-update-xenserver-licenses.in @@ -34,16 +34,19 @@ from threading import Thread # ---- 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) + "@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 cloud_utils import check_call, CalledProcessError, read_properties cfg = "@MSCONF@/db.properties" -#---------------------- option parsing and command line checks ------------------------ +# ---------------------- option parsing and command line checks ------------------------ usage = """%prog <-a | host names / IP addresses...> @@ -51,132 +54,171 @@ usage = """%prog <-a | host names / IP addresses...> This command deploys the license file specified in the command line into a specific XenServer host or all XenServer hosts known to the management server.""" parser = OptionParser(usage=usage) -parser.add_option("-a", "--all", action="store_true", dest="all", default=False, - help="deploy to all known hosts rather that a single host") +parser.add_option( + "-a", + "--all", + action="store_true", + dest="all", + default=False, + help="deploy to all known hosts rather that a single host", +) -#------------------ functions -------------------- +# ------------------ functions -------------------- -def e(msg): parser.error(msg) -def getknownhosts(host,username,password): - conn = mysql.connector.connect(host=host, user=username, password=password) - cur = conn.cursor() - cur.execute("SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'username' and setup = 1") - usernames = dict(cur.fetchall()) - cur.execute("SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'password' and setup = 1") - passwords = dict(cur.fetchall()) - creds = dict( [ [x,(usernames[x],passwords[x])] for x in list(usernames.keys()) ] ) - cur.close() - conn.close() - return creds +def e(msg): + parser.error(msg) + + +def getknownhosts(host, username, password): + conn = mysql.connector.connect(host=host, user=username, password=password) + cur = conn.cursor() + cur.execute( + "SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'username' and setup = 1" + ) + usernames = dict(cur.fetchall()) + cur.execute( + "SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'password' and setup = 1" + ) + passwords = dict(cur.fetchall()) + creds = dict([[x, (usernames[x], passwords[x])] for x in list(usernames.keys())]) + cur.close() + conn.close() + return creds + + +def splitlast(string, splitter): + splitted = string.split(splitter) + first, last = splitter.join(splitted[:-1]), splitted[-1] + return first, last -def splitlast(string,splitter): - splitted = string.split(splitter) - first,last = splitter.join(splitted[:-1]),splitted[-1] - return first,last def parseuserpwfromhosts(hosts): - creds = {} - for host in hosts: - user = "root" - password = "" - if "@" in host: - user,host = splitlast(host,"@") - if ":" in user: - user,password = splitlast(user,":") - creds[host] = (user,password) - return creds + creds = {} + for host in hosts: + user = "root" + password = "" + if "@" in host: + user, host = splitlast(host, "@") + if ":" in user: + user, password = splitlast(user, ":") + creds[host] = (user, password) + return creds + class XenServerConfigurator(Thread): - def __init__(self,host,user,password,keyfiledata): - Thread.__init__(self) - self.host = host - self.user = user - self.password = password - self.keyfiledata = keyfiledata - self.retval = None # means all's good - self.stdout = "" - self.stderr = "" - self.state = 'initialized' + def __init__(self, host, user, password, keyfiledata): + Thread.__init__(self) + self.host = host + self.user = user + self.password = password + self.keyfiledata = keyfiledata + self.retval = None # means all's good + self.stdout = "" + self.stderr = "" + self.state = "initialized" - def run(self): - try: - self.state = 'running' - c = paramiko.SSHClient() - c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - c.connect(self.host,username=self.user,password=self.password) - sftp = c.open_sftp() - sftp.chdir("/tmp") - f = sftp.open("xen-license","w") - f.write(self.keyfiledata) - f.close() - sftp.close() - stdin,stdout,stderr = c.exec_command("xe host-license-add license-file=/tmp/xen-license") - c.exec_command("false") - self.stdout = stdout.read(-1) - self.stderr = stderr.read(-1) - self.retval = stdin.channel.recv_exit_status() - c.close() - if self.retval != 0: self.state = 'failed' - else: self.state = 'finished' + def run(self): + try: + self.state = "running" + c = paramiko.SSHClient() + c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + c.connect(self.host, username=self.user, password=self.password) + sftp = c.open_sftp() + sftp.chdir("/tmp") + f = sftp.open("xen-license", "w") + f.write(self.keyfiledata) + f.close() + sftp.close() + stdin, stdout, stderr = c.exec_command( + "xe host-license-add license-file=/tmp/xen-license" + ) + c.exec_command("false") + self.stdout = stdout.read(-1) + self.stderr = stderr.read(-1) + self.retval = stdin.channel.recv_exit_status() + c.close() + if self.retval != 0: + self.state = "failed" + else: + self.state = "finished" - except Exception as e: - self.state = 'failed' - self.retval = e - #raise + except Exception as e: + self.state = "failed" + self.retval = e + # raise - def __str__(self): - if self.state == 'failed': - return "<%s XenServerConfigurator on %s@%s: %s>"%(self.state,self.user,self.host,str(self.retval)) - else: - return "<%s XenServerConfigurator on %s@%s>"%(self.state,self.user,self.host) + def __str__(self): + if self.state == "failed": + return "<%s XenServerConfigurator on %s@%s: %s>" % ( + self.state, + self.user, + self.host, + str(self.retval), + ) + else: + return "<%s XenServerConfigurator on %s@%s>" % ( + self.state, + self.user, + self.host, + ) -#------------- actual code -------------------- + +# ------------- actual code -------------------- (options, args) = parser.parse_args() try: - licensefile,args = args[0],args[1:] -except IndexError: e("The first argument must be the license file to use") + licensefile, args = args[0], args[1:] +except IndexError: + e("The first argument must be the license file to use") if options.all: - if len(args) != 0: e("IP addresses cannot be specified if -a is specified") - config = read_properties(cfg) - creds = getknownhosts(config["db.cloud.host"],config["db.cloud.username"],config["db.cloud.password"]) - hosts = list(creds.keys()) + if len(args) != 0: + e("IP addresses cannot be specified if -a is specified") + config = read_properties(cfg) + creds = getknownhosts( + config["db.cloud.host"], + config["db.cloud.username"], + config["db.cloud.password"], + ) + hosts = list(creds.keys()) else: - if not args: e("You must specify at least one IP address, or -a") - hosts = args - creds = parseuserpwfromhosts(hosts) + if not args: + e("You must specify at least one IP address, or -a") + hosts = args + creds = parseuserpwfromhosts(hosts) try: - keyfiledata = file(licensefile).read(-1) + keyfiledata = file(licensefile).read(-1) except OSError as e: - sys.stderr.write("The file %s cannot be opened"%licensefile) - sys.exit(1) + sys.stderr.write("The file %s cannot be opened" % licensefile) + sys.exit(1) configurators = [] -for host,(user,password) in list(creds.items()): - configurators.append ( XenServerConfigurator(host,user,password,keyfiledata ) ) +for host, (user, password) in list(creds.items()): + configurators.append(XenServerConfigurator(host, user, password, keyfiledata)) -for c in configurators: c.start() - for c in configurators: - print(c.host + "...", end=' ') - c.join() - if c.state == 'failed': - if c.retval: - msg = "failed with return code %s: %s%s"%(c.retval,c.stdout,c.stderr) - msg = msg.strip() - print(msg) - else: print("failed: %s"%c.retval) - else: - print("done") + c.start() -successes = len( [ a for a in configurators if not a.state == 'failed' ] ) -failures = len( [ a for a in configurators if a.state == 'failed' ] ) +for c in configurators: + print(c.host + "...", end=" ") + c.join() + if c.state == "failed": + if c.retval: + msg = "failed with return code %s: %s%s" % (c.retval, c.stdout, c.stderr) + msg = msg.strip() + print(msg) + else: + print("failed: %s" % c.retval) + else: + print("done") -print("%3s successes"%successes) -print("%3s failures"%failures) +successes = len([a for a in configurators if not a.state == "failed"]) +failures = len([a for a in configurators if a.state == "failed"]) + +print("%3s successes" % successes) +print("%3s failures" % failures) diff --git a/client/conf/server.properties.in b/client/conf/server.properties.in index 0a6078048d3..5958486b4df 100644 --- a/client/conf/server.properties.in +++ b/client/conf/server.properties.in @@ -55,3 +55,10 @@ webapp.dir=/usr/share/cloudstack-management/webapp # The path to access log file access.log=/var/log/cloudstack/management/access.log + +# The deployment mode for the extensions +extensions.deployment.mode=@EXTENSIONSDEPLOYMENTMODE@ + +# Thread pool configuration +#threads.min=10 +#threads.max=500 diff --git a/client/pom.xml b/client/pom.xml index 2b84a680b13..72181442b43 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT @@ -321,16 +321,6 @@ cloud-plugin-hypervisor-ucs ${project.version} - - org.apache.cloudstack - cloud-plugin-hypervisor-ovm - ${project.version} - - - org.apache.cloudstack - cloud-plugin-hypervisor-ovm3 - ${project.version} - org.apache.cloudstack cloud-plugin-hypervisor-kvm @@ -347,6 +337,11 @@ cloud-plugin-hypervisor-hyperv ${project.version} + + org.apache.cloudstack + cloud-plugin-hypervisor-external + ${project.version} + org.apache.cloudstack cloud-plugin-storage-allocator-random @@ -647,6 +642,11 @@ cloud-plugin-storage-object-ceph ${project.version} + + org.apache.cloudstack + cloud-plugin-storage-object-cloudian + ${project.version} + org.apache.cloudstack cloud-plugin-storage-object-simulator @@ -1102,6 +1102,11 @@ cloud-plugin-network-nsx ${project.version} + + org.apache.cloudstack + cloud-plugin-network-netris + ${project.version} + org.apache.cloudstack cloud-plugin-network-tungsten diff --git a/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java new file mode 100644 index 00000000000..123d2761e00 --- /dev/null +++ b/client/src/main/java/org/apache/cloudstack/ACSRequestLog.java @@ -0,0 +1,84 @@ +// +// 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; + +import com.cloud.utils.StringUtils; +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.DateCache; +import org.eclipse.jetty.util.component.LifeCycle; + +import java.util.Locale; +import java.util.TimeZone; + +import static org.apache.commons.configuration.DataConfiguration.DEFAULT_DATE_FORMAT; + +public class ACSRequestLog extends NCSARequestLog { + private static final ThreadLocal buffers = + ThreadLocal.withInitial(() -> new StringBuilder(256)); + + private final DateCache dateCache; + + public ACSRequestLog() { + super(); + + TimeZone timeZone = TimeZone.getTimeZone("GMT"); + Locale locale = Locale.getDefault(); + dateCache = new DateCache(DEFAULT_DATE_FORMAT, locale, timeZone); + } + + @Override + public void log(Request request, Response response) { + String requestURI = StringUtils.cleanString(request.getOriginalURI()); + try { + StringBuilder sb = buffers.get(); + sb.setLength(0); + + sb.append(request.getHttpChannel().getEndPoint() + .getRemoteAddress().getAddress() + .getHostAddress()) + .append(" - - [") + .append(dateCache.format(request.getTimeStamp())) + .append("] \"") + .append(request.getMethod()) + .append(" ") + .append(requestURI) + .append(" ") + .append(request.getProtocol()) + .append("\" ") + .append(response.getStatus()) + .append(" ") + .append(response.getHttpChannel().getBytesWritten()) // apply filter here? + .append(" \"-\" \"") + .append(request.getHeader("User-Agent")) + .append("\""); + + write(sb.toString()); + } catch (Exception e) { + LOG.warn("Unable to log request", e); + } + } + + @Override + protected void stop(LifeCycle lifeCycle) throws Exception { + buffers.remove(); + super.stop(lifeCycle); + } +} diff --git a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java index c6fd2ff24dc..196695e1fc6 100644 --- a/client/src/main/java/org/apache/cloudstack/ServerDaemon.java +++ b/client/src/main/java/org/apache/cloudstack/ServerDaemon.java @@ -24,15 +24,17 @@ import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.net.URL; +import java.util.Arrays; import java.util.Properties; +import com.cloud.api.ApiServer; import org.apache.commons.daemon.Daemon; import org.apache.commons.daemon.DaemonContext; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; @@ -84,6 +86,8 @@ public class ServerDaemon implements Daemon { private static final int DEFAULT_REQUEST_CONTENT_SIZE = 1048576; private static final String REQUEST_MAX_FORM_KEYS_KEY = "request.max.form.keys"; private static final int DEFAULT_REQUEST_MAX_FORM_KEYS = 5000; + private static final String THREADS_MIN = "threads.min"; + private static final String THREADS_MAX = "threads.max"; //////////////////////////////////////////////////////// /////////////// Server Configuration /////////////////// @@ -104,6 +108,8 @@ public class ServerDaemon implements Daemon { private String keystoreFile; private String keystorePassword; private String webAppLocation; + private int minThreads; + private int maxThreads; ////////////////////////////////////////////////// /////////////// Public methods /////////////////// @@ -145,6 +151,8 @@ public class ServerDaemon implements Daemon { setSessionTimeout(Integer.valueOf(properties.getProperty(SESSION_TIMEOUT, "30"))); setMaxFormContentSize(Integer.valueOf(properties.getProperty(REQUEST_CONTENT_SIZE_KEY, String.valueOf(DEFAULT_REQUEST_CONTENT_SIZE)))); setMaxFormKeys(Integer.valueOf(properties.getProperty(REQUEST_MAX_FORM_KEYS_KEY, String.valueOf(DEFAULT_REQUEST_MAX_FORM_KEYS)))); + setMinThreads(Integer.valueOf(properties.getProperty(THREADS_MIN, "10"))); + setMaxThreads(Integer.valueOf(properties.getProperty(THREADS_MAX, "500"))); } catch (final IOException e) { logger.warn("Failed to read configuration from server.properties file", e); } finally { @@ -162,8 +170,8 @@ public class ServerDaemon implements Daemon { public void start() throws Exception { // Thread pool final QueuedThreadPool threadPool = new QueuedThreadPool(); - threadPool.setMinThreads(10); - threadPool.setMaxThreads(500); + threadPool.setMinThreads(minThreads); + threadPool.setMaxThreads(maxThreads); // Jetty Server server = new Server(threadPool); @@ -185,6 +193,7 @@ public class ServerDaemon implements Daemon { httpConfig.setResponseHeaderSize(8192); httpConfig.setSendServerVersion(false); httpConfig.setSendDateHeader(false); + addForwardingCustomiser(httpConfig); // HTTP Connector createHttpConnector(httpConfig); @@ -207,6 +216,21 @@ public class ServerDaemon implements Daemon { server.join(); } + /** + * Adds a ForwardedRequestCustomizer to the HTTP configuration to handle forwarded headers. + * The header used for forwarding is determined by the ApiServer.listOfForwardHeaders property. + * Only non empty headers are considered and only the first of the comma-separated list is used. + * @param httpConfig the HTTP configuration to which the customizer will be added + */ + private static void addForwardingCustomiser(HttpConfiguration httpConfig) { + ForwardedRequestCustomizer customiser = new ForwardedRequestCustomizer(); + String header = Arrays.stream(ApiServer.listOfForwardHeaders.value().split(",")).findFirst().orElse(null); + if (com.cloud.utils.StringUtils.isNotEmpty(header)) { + customiser.setForwardedForHeader(header); + } + httpConfig.addCustomizer(customiser); + } + @Override public void stop() throws Exception { server.stop(); @@ -299,7 +323,7 @@ public class ServerDaemon implements Daemon { } private RequestLog createRequestLog() { - final NCSARequestLog log = new NCSARequestLog(); + final ACSRequestLog log = new ACSRequestLog(); final File logPath = new File(accessLogFile); final File parentFile = logPath.getParentFile(); if (parentFile != null) { @@ -376,4 +400,12 @@ public class ServerDaemon implements Daemon { public void setMaxFormKeys(int maxFormKeys) { this.maxFormKeys = maxFormKeys; } + + public void setMinThreads(int minThreads) { + this.minThreads = minThreads; + } + + public void setMaxThreads(int maxThreads) { + this.maxThreads = maxThreads; + } } diff --git a/cloud-cli/bindir/cloud-tool b/cloud-cli/bindir/cloud-tool index 410681a0dd5..0f0815307a1 100755 --- a/cloud-cli/bindir/cloud-tool +++ b/cloud-cli/bindir/cloud-tool @@ -7,9 +7,9 @@ # 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 @@ -25,4 +25,5 @@ sys.path.append(os.path.dirname(os.path.dirname(__file__))) import cloudtool ret = cloudtool.main() -if ret: sys.exit(ret) +if ret: + sys.exit(ret) diff --git a/core/pom.xml b/core/pom.xml index 25ac46518b3..e4cd84357d1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT diff --git a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java index b299c602dde..351702a048c 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckS2SVpnConnectionsAnswer.java @@ -25,7 +25,6 @@ import java.util.Map; public class CheckS2SVpnConnectionsAnswer extends Answer { Map ipToConnected; Map ipToDetail; - String details; protected CheckS2SVpnConnectionsAnswer() { ipToConnected = new HashMap(); diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java index 5a32ab59a7a..07b7e102df9 100644 --- a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java @@ -17,22 +17,33 @@ package com.cloud.agent.api; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.Map; + public class CheckVolumeAnswer extends Answer { private long size; + private Map volumeDetails; CheckVolumeAnswer() { } - public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) { - super(cmd, true, details); + public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size, + Map volumeDetails) { + super(cmd, success, details); this.size = size; + this.volumeDetails = volumeDetails; } public long getSize() { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CheckVolumeAnswer [size=" + size + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java index 174348f4f18..8092ab9b43f 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java @@ -16,8 +16,6 @@ // under the License. package com.cloud.agent.api; -import org.apache.cloudstack.vm.UnmanagedInstanceTO; - public class ConvertInstanceAnswer extends Answer { private String temporaryConvertUuid; @@ -25,16 +23,6 @@ public class ConvertInstanceAnswer extends Answer { public ConvertInstanceAnswer() { super(); } - private UnmanagedInstanceTO convertedInstance; - - public ConvertInstanceAnswer(Command command, boolean success, String details) { - super(command, success, details); - } - - public ConvertInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) { - super(command, true, ""); - this.convertedInstance = convertedInstance; - } public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) { super(command, true, ""); @@ -44,8 +32,4 @@ public class ConvertInstanceAnswer extends Answer { public String getTemporaryConvertUuid() { return temporaryConvertUuid; } - - public UnmanagedInstanceTO getConvertedInstance() { - return convertedInstance; - } } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java index b8250903f85..f938d0ac55d 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -20,13 +20,10 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.RemoteInstanceTO; import com.cloud.hypervisor.Hypervisor; -import java.util.List; - public class ConvertInstanceCommand extends Command { private RemoteInstanceTO sourceInstance; private Hypervisor.HypervisorType destinationHypervisorType; - private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; private String templateDirOnConversionLocation; private boolean checkConversionSupport; @@ -36,12 +33,10 @@ public class ConvertInstanceCommand extends Command { public ConvertInstanceCommand() { } - public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation, + public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, DataStoreTO conversionTemporaryLocation, String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; - this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; this.templateDirOnConversionLocation = templateDirOnConversionLocation; this.checkConversionSupport = checkConversionSupport; @@ -56,10 +51,6 @@ public class ConvertInstanceCommand extends Command { return destinationHypervisorType; } - public List getDestinationStoragePools() { - return destinationStoragePools; - } - public DataStoreTO getConversionTemporaryLocation() { return conversionTemporaryLocation; } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java new file mode 100644 index 00000000000..c19a061f736 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotAnswer.java @@ -0,0 +1,36 @@ +/* + * 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; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotAnswer extends Answer { + + private SnapshotObjectTO snapshotObjectTO; + + public ConvertSnapshotAnswer(SnapshotObjectTO snapshotObjectTO) { + super(null); + this.snapshotObjectTO = snapshotObjectTO; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java new file mode 100644 index 00000000000..e456d4b6b12 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/ConvertSnapshotCommand.java @@ -0,0 +1,42 @@ +/* + * 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; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class ConvertSnapshotCommand extends Command { + + public static final String TEMP_SNAPSHOT_NAME = "_temp"; + + SnapshotObjectTO snapshotObjectTO; + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public ConvertSnapshotCommand(SnapshotObjectTO snapshotObjectTO) { + this.snapshotObjectTO = snapshotObjectTO; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java index e79005be71b..4aec0b26581 100644 --- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java @@ -17,21 +17,28 @@ package com.cloud.agent.api; +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.Map; + public class CopyRemoteVolumeAnswer extends Answer { private String remoteIp; private String filename; private long size; + private Map volumeDetails; CopyRemoteVolumeAnswer() { } - public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) { - super(cmd, true, details); + public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size, + Map volumeDetails) { + super(cmd, success, details); this.remoteIp = cmd.getRemoteIp(); this.filename = filename; this.size = size; + this.volumeDetails = volumeDetails; } public String getRemoteIp() { @@ -54,6 +61,10 @@ public class CopyRemoteVolumeAnswer extends Answer { return size; } + public Map getVolumeDetails() { + return volumeDetails; + } + public String getString() { return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]"; } diff --git a/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java index 969dd2eb801..84ca39f1546 100644 --- a/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/DeleteStoragePoolCommand.java @@ -21,10 +21,10 @@ package com.cloud.agent.api; import java.io.File; import java.util.Map; -import java.util.UUID; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.storage.StoragePool; +import com.cloud.utils.UuidUtils; public class DeleteStoragePoolCommand extends Command { public static final String DATASTORE_NAME = "datastoreName"; @@ -49,7 +49,7 @@ public class DeleteStoragePoolCommand extends Command { } public DeleteStoragePoolCommand(StoragePool pool) { - this(pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); + this(pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); } public void setPool(StoragePool pool) { diff --git a/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java index 8b3cd44e207..5bf70ed086f 100644 --- a/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/GetGPUStatsAnswer.java @@ -19,7 +19,9 @@ package com.cloud.agent.api; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import com.cloud.agent.api.LogLevel.Log4jLevel; @@ -27,6 +29,7 @@ import com.cloud.agent.api.LogLevel.Log4jLevel; public class GetGPUStatsAnswer extends Answer { private HashMap> groupDetails; + private List gpuDevices = new ArrayList<>(); public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final HashMap> groupDetails) { super(cmd); @@ -37,7 +40,21 @@ public class GetGPUStatsAnswer extends Answer { super(cmd, success, details); } + public GetGPUStatsAnswer(final GetGPUStatsCommand cmd, final List gpuDevices) { + super(cmd); + this.gpuDevices = gpuDevices; + } + + public HashMap> getGroupDetails() { return groupDetails; } + + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } } diff --git a/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java b/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java index a9c7413b9f0..4efc0781f91 100644 --- a/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java +++ b/core/src/main/java/com/cloud/agent/api/GetVmIpAddressCommand.java @@ -24,11 +24,13 @@ public class GetVmIpAddressCommand extends Command { String vmName; String vmNetworkCidr; boolean windows = false; + String macAddress; - public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows) { + public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows, String macAddress) { this.vmName = vmName; this.windows = windows; this.vmNetworkCidr = vmNetworkCidr; + this.macAddress = macAddress; } @Override @@ -47,4 +49,8 @@ public class GetVmIpAddressCommand extends Command { public String getVmNetworkCidr() { return vmNetworkCidr; } + + public String getMacAddress() { + return macAddress; + } } diff --git a/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java b/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java new file mode 100644 index 00000000000..16942bb05d4 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/HandleCksIsoCommand.java @@ -0,0 +1,34 @@ +// +// 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; + +import com.cloud.agent.api.routing.NetworkElementCommand; + +public class HandleCksIsoCommand extends NetworkElementCommand { + + private boolean mountCksIso; + + public HandleCksIsoCommand(boolean mountCksIso) { + this.mountCksIso = mountCksIso; + } + + public boolean isMountCksIso() { + return mountCksIso; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java index 3acdb9c351b..5ac4e9ae445 100644 --- a/core/src/main/java/com/cloud/agent/api/MigrateCommand.java +++ b/core/src/main/java/com/cloud/agent/api/MigrateCommand.java @@ -29,14 +29,14 @@ import com.cloud.agent.api.to.VirtualMachineTO; public class MigrateCommand extends Command { private String vmName; - private String destIp; + private String destinationIp; private Map migrateStorage; private boolean migrateStorageManaged; private boolean migrateNonSharedInc; private boolean autoConvergence; private String hostGuid; - private boolean isWindows; - private VirtualMachineTO vmTO; + private boolean windows; + private VirtualMachineTO virtualMachine; private boolean executeInSequence = false; private List migrateDiskInfoList = new ArrayList<>(); private Map dpdkInterfaceMapping = new HashMap<>(); @@ -64,11 +64,11 @@ public class MigrateCommand extends Command { protected MigrateCommand() { } - public MigrateCommand(String vmName, String destIp, boolean isWindows, VirtualMachineTO vmTO, boolean executeInSequence) { + public MigrateCommand(String vmName, String destinationIp, boolean windows, VirtualMachineTO virtualMachine, boolean executeInSequence) { this.vmName = vmName; - this.destIp = destIp; - this.isWindows = isWindows; - this.vmTO = vmTO; + this.destinationIp = destinationIp; + this.windows = windows; + this.virtualMachine = virtualMachine; this.executeInSequence = executeInSequence; } @@ -105,15 +105,15 @@ public class MigrateCommand extends Command { } public boolean isWindows() { - return isWindows; + return windows; } public VirtualMachineTO getVirtualMachine() { - return vmTO; + return virtualMachine; } public String getDestinationIp() { - return destIp; + return destinationIp; } public String getVmName() { @@ -233,4 +233,9 @@ public class MigrateCommand extends Command { this.isSourceDiskOnStorageFileSystem = isDiskOnFileSystemStorage; } } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java index 552ffb85aaf..a3401d6faed 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolAnswer.java @@ -46,6 +46,10 @@ public class ModifyStoragePoolAnswer extends Answer { templateInfo = tInfo; } + public ModifyStoragePoolAnswer(ModifyStoragePoolCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + public void setPoolInfo(StoragePoolInfo poolInfo) { this.poolInfo = poolInfo; } diff --git a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java index 06940266b53..a72000d7399 100644 --- a/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ModifyStoragePoolCommand.java @@ -21,10 +21,10 @@ package com.cloud.agent.api; import java.io.File; import java.util.Map; -import java.util.UUID; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.storage.StoragePool; +import com.cloud.utils.UuidUtils; public class ModifyStoragePoolCommand extends Command { public static final String LOCAL_PATH_PREFIX = "/mnt/"; @@ -47,11 +47,11 @@ public class ModifyStoragePoolCommand extends Command { } public ModifyStoragePoolCommand(boolean add, StoragePool pool, Map details) { - this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()), details); + this(add, pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes()), details); } public ModifyStoragePoolCommand(boolean add, StoragePool pool) { - this(add, pool, LOCAL_PATH_PREFIX + File.separator + UUID.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); + this(add, pool, LOCAL_PATH_PREFIX + File.separator + UuidUtils.nameUUIDFromBytes((pool.getHostAddress() + pool.getPath()).getBytes())); } public boolean getAdd() { diff --git a/core/src/main/java/com/cloud/agent/api/PingAnswer.java b/core/src/main/java/com/cloud/agent/api/PingAnswer.java index 6353b121583..22eb4443f8a 100644 --- a/core/src/main/java/com/cloud/agent/api/PingAnswer.java +++ b/core/src/main/java/com/cloud/agent/api/PingAnswer.java @@ -19,18 +19,25 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; + public class PingAnswer extends Answer { private PingCommand _command = null; private boolean sendStartup = false; + private List avoidMsList; + + private List reconcileCommands = new ArrayList<>(); protected PingAnswer() { } - public PingAnswer(PingCommand cmd, boolean sendStartup) { + public PingAnswer(PingCommand cmd, List avoidMsList, boolean sendStartup) { super(cmd); _command = cmd; this.sendStartup = sendStartup; + this.avoidMsList = avoidMsList; } public PingCommand getCommand() { @@ -44,4 +51,20 @@ public class PingAnswer extends Answer { public void setSendStartup(boolean sendStartup) { this.sendStartup = sendStartup; } + + public List getReconcileCommands() { + return reconcileCommands; + } + + public void setReconcileCommands(List reconcileCommands) { + this.reconcileCommands = reconcileCommands; + } + + public void addReconcileCommand(String reconcileCommand) { + this.reconcileCommands.add(reconcileCommand); + } + + public List getAvoidMsList() { + return avoidMsList; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PingCommand.java b/core/src/main/java/com/cloud/agent/api/PingCommand.java index 4192fc2e747..4ae64da89c3 100644 --- a/core/src/main/java/com/cloud/agent/api/PingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/PingCommand.java @@ -20,11 +20,14 @@ package com.cloud.agent.api; import com.cloud.host.Host; +import org.apache.cloudstack.command.CommandInfo; public class PingCommand extends Command { Host.Type hostType; long hostId; boolean outOfBand; + @LogLevel(LogLevel.Log4jLevel.Trace) + private CommandInfo[] commandInfos = new CommandInfo[] {}; protected PingCommand() { } @@ -78,4 +81,12 @@ public class PingCommand extends Command { result = 31 * result + (int) (hostId ^ (hostId >>> 32)); return result; } + + public CommandInfo[] getCommandInfos() { + return commandInfos; + } + + public void setCommandInfos(CommandInfo[] commandInfos) { + this.commandInfos = commandInfos; + } } diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java new file mode 100644 index 00000000000..b94d18c537e --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningAnswer.java @@ -0,0 +1,51 @@ +// +// 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; + +import java.util.Map; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningAnswer extends Answer { + + Map serverDetails; + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningAnswer() { + super(); + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, Map externalDetails, VirtualMachineTO virtualMachineTO, String details) { + super(cmd, true, details); + this.serverDetails = externalDetails; + this.virtualMachineTO = virtualMachineTO; + } + + public PrepareExternalProvisioningAnswer(PrepareExternalProvisioningCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + public Map getServerDetails() { + return serverDetails; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java new file mode 100644 index 00000000000..44f57607eba --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/PrepareExternalProvisioningCommand.java @@ -0,0 +1,39 @@ +// +// 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; + +import com.cloud.agent.api.to.VirtualMachineTO; + +public class PrepareExternalProvisioningCommand extends Command { + + VirtualMachineTO virtualMachineTO; + + public PrepareExternalProvisioningCommand(VirtualMachineTO vmTO) { + this.virtualMachineTO = vmTO; + } + + public VirtualMachineTO getVirtualMachineTO() { + return virtualMachineTO; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java index e2d974e3878..ee61fee66c6 100644 --- a/core/src/main/java/com/cloud/agent/api/ReadyCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ReadyCommand.java @@ -26,20 +26,21 @@ import java.util.List; public class ReadyCommand extends Command { private String _details; - public ReadyCommand() { - super(); - } - private Long dcId; private Long hostId; private String hostUuid; private String hostName; private List msHostList; + private List avoidMsHostList; private String lbAlgorithm; private Long lbCheckInterval; private Boolean enableHumanReadableSizes; private String arch; + public ReadyCommand() { + super(); + } + public ReadyCommand(Long dcId) { super(); this.dcId = dcId; @@ -90,6 +91,14 @@ public class ReadyCommand extends Command { this.msHostList = msHostList; } + public List getAvoidMsHostList() { + return avoidMsHostList; + } + + public void setAvoidMsHostList(List avoidMsHostList) { + this.avoidMsHostList = avoidMsHostList; + } + public String getLbAlgorithm() { return lbAlgorithm; } diff --git a/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java b/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java new file mode 100644 index 00000000000..154b611be98 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RecreateCheckpointsCommand.java @@ -0,0 +1,49 @@ +// +// 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; + +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RecreateCheckpointsCommand extends Command { + + private List volumes; + + private String vmName; + + public RecreateCheckpointsCommand(List volumes, String vmName) { + this.volumes = volumes; + this.vmName = vmName; + } + + public List getDisks() { + return volumes; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java new file mode 100644 index 00000000000..90b62e3f17b --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RemoveBitmapCommand.java @@ -0,0 +1,46 @@ +// +// 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; + +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +public class RemoveBitmapCommand extends Command { + + private SnapshotObjectTO snapshotObjectTO; + + private boolean isVmRunning; + + public RemoveBitmapCommand(SnapshotObjectTO snapshotObjectTO, boolean isVmRunning) { + this.snapshotObjectTO = snapshotObjectTO; + this.isVmRunning = isVmRunning; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public SnapshotObjectTO getSnapshotObjectTO() { + return snapshotObjectTO; + } + + public boolean isVmRunning() { + return isVmRunning; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java new file mode 100644 index 00000000000..1deb789c995 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionAnswer.java @@ -0,0 +1,32 @@ +// +// 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 RunCustomActionAnswer extends Answer { + + public RunCustomActionAnswer(RunCustomActionCommand cmd, boolean success, String details) { + super(cmd, success, details); + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java new file mode 100644 index 00000000000..36489ad4fa5 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/RunCustomActionCommand.java @@ -0,0 +1,59 @@ +// +// 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; + +import java.util.Map; + +public class RunCustomActionCommand extends Command { + + String actionName; + Long vmId; + Map parameters; + + public RunCustomActionCommand(String actionName) { + this.actionName = actionName; + this.setWait(5); + } + + public String getActionName() { + return actionName; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java index 06583f2d0d3..fef0ceed6b8 100644 --- a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java +++ b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java @@ -36,6 +36,7 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { String routerIpv6Gateway = null; String routerIpv6Cidr = null; boolean isVrGuestGateway = false; + long networkId; public NicTO getNic() { return nic; @@ -123,4 +124,12 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { public void setVrGuestGateway(boolean vrGuestGateway) { isVrGuestGateway = vrGuestGateway; } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java index 286fced0c58..068196aabe5 100644 --- a/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StartupRoutingCommand.java @@ -45,6 +45,7 @@ public class StartupRoutingCommand extends StartupCommand { List hostTags = new ArrayList(); String hypervisorVersion; HashMap> groupDetails = new HashMap>(); + List gpuDevices = new ArrayList<>(); private Boolean hostHealthCheckResult; public StartupRoutingCommand() { @@ -179,7 +180,7 @@ public class StartupRoutingCommand extends StartupCommand { this.hostTags = hostTags; } - public HashMap> getGpuGroupDetails() { + public HashMap> getGpuGroupDetails() { return groupDetails; } @@ -187,6 +188,14 @@ public class StartupRoutingCommand extends StartupCommand { this.groupDetails = groupDetails; } + public List getGpuDevices() { + return gpuDevices; + } + + public void setGpuDevices(List gpuDevices) { + this.gpuDevices = gpuDevices; + } + public boolean getSupportsClonedVolumes() { return supportsClonedVolumes; } diff --git a/core/src/main/java/com/cloud/agent/api/StopCommand.java b/core/src/main/java/com/cloud/agent/api/StopCommand.java index 3923a35bd0a..d07ffa2e31f 100644 --- a/core/src/main/java/com/cloud/agent/api/StopCommand.java +++ b/core/src/main/java/com/cloud/agent/api/StopCommand.java @@ -19,14 +19,14 @@ package com.cloud.agent.api; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import com.cloud.agent.api.to.DpdkTO; import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.vm.VirtualMachine; -import java.util.ArrayList; -import java.util.Map; -import java.util.List; - public class StopCommand extends RebootCommand { private boolean isProxy = false; private String urlPort = null; @@ -37,6 +37,7 @@ public class StopCommand extends RebootCommand { boolean forceStop = false; private Map dpdkInterfaceMapping; Map vlanToPersistenceMap; + boolean expungeVM = false; public Map getDpdkInterfaceMapping() { return dpdkInterfaceMapping; @@ -138,4 +139,12 @@ public class StopCommand extends RebootCommand { public void setVlanToPersistenceMap(Map vlanToPersistenceMap) { this.vlanToPersistenceMap = vlanToPersistenceMap; } + + public boolean isExpungeVM() { + return expungeVM; + } + + public void setExpungeVM(boolean expungeVM) { + this.expungeVM = expungeVM; + } } diff --git a/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java index bebd30ca519..b98ce6fa345 100644 --- a/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java +++ b/core/src/main/java/com/cloud/agent/api/UnprepareStorageClientCommand.java @@ -19,18 +19,22 @@ package com.cloud.agent.api; +import java.util.Map; + import com.cloud.storage.Storage.StoragePoolType; public class UnprepareStorageClientCommand extends Command { private StoragePoolType poolType; private String poolUuid; + private Map details; public UnprepareStorageClientCommand() { } - public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid) { + public UnprepareStorageClientCommand(StoragePoolType poolType, String poolUuid, Map details) { this.poolType = poolType; this.poolUuid = poolUuid; + this.details = details; } @Override @@ -45,4 +49,8 @@ public class UnprepareStorageClientCommand extends Command { public String getPoolUuid() { return poolUuid; } + + public Map getDetails() { + return details; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 00000000000..4d61249c7cb --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,39 @@ +/* + * 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.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.utils.Pair; + +import java.util.Map; + +public class CreateDiskOnlyVmSnapshotAnswer extends Answer { + + protected Map> mapVolumeToSnapshotSizeAndNewVolumePath; + + public CreateDiskOnlyVmSnapshotAnswer(Command command, boolean success, String details, Map> mapVolumeToSnapshotSizeAndNewVolumePath) { + super(command, success, details); + this.mapVolumeToSnapshotSizeAndNewVolumePath = mapVolumeToSnapshotSizeAndNewVolumePath; + } + + public Map> getMapVolumeToSnapshotSizeAndNewVolumePath() { + return mapVolumeToSnapshotSizeAndNewVolumePath; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..952bf0c971d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/CreateDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,41 @@ +/* + * 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.storage; + + +import com.cloud.agent.api.VMSnapshotBaseCommand; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class CreateDiskOnlyVmSnapshotCommand extends VMSnapshotBaseCommand { + + protected VirtualMachine.State vmState; + + public CreateDiskOnlyVmSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { + super(vmName, snapshot, volumeTOs, guestOSType); + this.vmState = vmState; + } + + public VirtualMachine.State getVmState() { + return vmState; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..bf7bdd59736 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/DeleteDiskOnlyVmSnapshotCommand.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.storage; + +import com.cloud.agent.api.Command; + +import com.cloud.agent.api.to.DataTO; + + +import java.util.List; + +public class DeleteDiskOnlyVmSnapshotCommand extends Command { + + List snapshots; + + public DeleteDiskOnlyVmSnapshotCommand(List snapshots) { + this.snapshots = snapshots; + } + + public List getSnapshots() { + return snapshots; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..b6396c24d10 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/MergeDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,55 @@ +/* + * 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.storage; + +import com.cloud.agent.api.Command; +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class MergeDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotMergeTreeToList; + private VirtualMachine.State vmState; + private String vmName; + + public MergeDiskOnlyVmSnapshotCommand(List snapshotMergeTreeToList, VirtualMachine.State vmState, String vmName) { + this.snapshotMergeTreeToList = snapshotMergeTreeToList; + this.vmState = vmState; + this.vmName = vmName; + } + + public List getSnapshotMergeTreeToList() { + return snapshotMergeTreeToList; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java index 9fed0f913e1..70375c30a1b 100644 --- a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java +++ b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java @@ -145,4 +145,9 @@ public class MigrateVolumeCommand extends Command { } public String getChainInfo() { return chainInfo; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java new file mode 100644 index 00000000000..2ecf587d59d --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotAnswer.java @@ -0,0 +1,38 @@ +/* + * 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.storage; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotAnswer extends Answer { + List volumeObjectTos; + + public RevertDiskOnlyVmSnapshotAnswer(Command cmd, List volumeObjectTos) { + super(cmd, true, null); + this.volumeObjectTos = volumeObjectTos; + } + + public List getVolumeObjectTos() { + return volumeObjectTos; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java new file mode 100644 index 00000000000..72bb92bcb10 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/RevertDiskOnlyVmSnapshotCommand.java @@ -0,0 +1,50 @@ +/* + * 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.storage; + +import com.cloud.agent.api.Command; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +import java.util.List; + +public class RevertDiskOnlyVmSnapshotCommand extends Command { + + private List snapshotObjectTos; + private String vmName; + + public RevertDiskOnlyVmSnapshotCommand(List snapshotObjectTos, String vmName) { + super(); + this.snapshotObjectTos = snapshotObjectTos; + this.vmName = vmName; + } + + public List getSnapshotObjectTos() { + return snapshotObjectTos; + } + + public String getVmName() { + return vmName; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java new file mode 100644 index 00000000000..78f23105e19 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/storage/SnapshotMergeTreeTO.java @@ -0,0 +1,57 @@ +/* + * 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.storage; + +import com.cloud.agent.api.to.DataTO; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; + +import java.util.List; + +public class SnapshotMergeTreeTO { + DataTO parent; + DataTO child; + List grandChildren; + + public SnapshotMergeTreeTO(DataTO parent, DataTO child, List grandChildren) { + this.parent = parent; + this.child = child; + this.grandChildren = grandChildren; + } + + public DataTO getParent() { + return parent; + } + + public DataTO getChild() { + return child; + } + + public List getGrandChildren() { + return grandChildren; + } + + public void addGrandChild(DataTO grandChild) { + grandChildren.add(grandChild); + } + + @Override + public String toString() { + return ReflectionToStringBuilder.toString(this); + } +} diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java index f9ea3e05e97..7bfbf786e9b 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VRScripts.java @@ -82,5 +82,8 @@ public class VRScripts { public static final String VR_UPDATE_INTERFACE_CONFIG = "update_interface_config.sh"; public static final String ROUTER_FILESYSTEM_WRITABLE_CHECK = "filesystem_writable_check.py"; + + // CKS ISO mount + public static final String CKS_ISO_MOUNT_SERVE = "cks_iso.sh"; public static final String MANAGE_SERVICE = "manage_service.sh"; } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 4afac9b43cb..bd632632ae8 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantLock; import javax.naming.ConfigurationException; +import com.cloud.agent.api.HandleCksIsoCommand; import org.apache.cloudstack.agent.routing.ManageServiceCommand; import com.cloud.agent.api.routing.UpdateNetworkCommand; import com.cloud.agent.api.to.IpAddressTO; @@ -145,6 +146,10 @@ public class VirtualRoutingResource { return execute((UpdateNetworkCommand) cmd); } + if (cmd instanceof HandleCksIsoCommand) { + return execute((HandleCksIsoCommand) cmd); + } + if (cmd instanceof ManageServiceCommand) { return execute((ManageServiceCommand) cmd); } @@ -176,6 +181,13 @@ public class VirtualRoutingResource { } } + protected Answer execute(final HandleCksIsoCommand cmd) { + String routerIp = getRouterSshControlIp(cmd); + logger.info("Attempting to mount CKS ISO on Virtual Router"); + ExecutionResult result = _vrDeployer.executeInVR(routerIp, VRScripts.CKS_ISO_MOUNT_SERVE, String.valueOf(cmd.isMountCksIso())); + return new Answer(cmd, result.isSuccess(), result.getDetails()); + } + private Answer execute(final SetupKeyStoreCommand cmd) { final String args = String.format("/usr/local/cloud/systemvm/conf/agent.properties " + "/usr/local/cloud/systemvm/conf/%s " + diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java index 4832c906699..6dae886b413 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/LoadBalancerConfigItem.java @@ -56,6 +56,8 @@ public class LoadBalancerConfigItem extends AbstractConfigItemFacade { final String[] statRules = allRules[LoadBalancerConfigurator.STATS]; final LoadBalancerRule loadBalancerRule = new LoadBalancerRule(configuration, tmpCfgFilePath, tmpCfgFileName, addRules, removeRules, statRules, routerIp); + final LoadBalancerRule.SslCertEntry[] sslCerts = cfgtr.generateSslCertEntries(command); + loadBalancerRule.setSslCerts(sslCerts); final List rules = new LinkedList(); rules.add(loadBalancerRule); diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java index 1a6824ceb7f..57c96ec4bc9 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java @@ -76,6 +76,7 @@ public class SetGuestNetworkConfigItem extends AbstractConfigItemFacade { guestNetwork.setRouterIp6Gateway(command.getRouterIpv6Gateway()); guestNetwork.setRouterIp6Cidr(command.getRouterIpv6Cidr()); guestNetwork.setVrGuestGateway(command.isVrGuestGateway()); + guestNetwork.setNetworkId(command.getNetworkId()); return generateConfigItems(guestNetwork); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java index a416b4bc5e4..6bf36d62bf9 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java @@ -38,6 +38,7 @@ public class GuestNetwork extends ConfigBase { private String routerIp6Gateway; private String routerIp6Cidr; private boolean isVrGuestGateway; + long networkId; private Integer mtu; @@ -211,4 +212,12 @@ public class GuestNetwork extends ConfigBase { public void setVrGuestGateway(boolean vrGuestGateway) { isVrGuestGateway = vrGuestGateway; } + + public long getNetworkId() { + return networkId; + } + + public void setNetworkId(long networkId) { + this.networkId = networkId; + } } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java index 3743d608e6c..361c4765cc5 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRule.java @@ -25,6 +25,7 @@ public class LoadBalancerRule { private String[] configuration; private String tmpCfgFilePath; private String tmpCfgFileName; + private SslCertEntry[] sslCerts; private String[] addRules; private String[] removeRules; @@ -32,6 +33,53 @@ public class LoadBalancerRule { private String routerIp; + public static class SslCertEntry { + private String name; + private String cert; + private String key; + private String chain; + private String password; + + public SslCertEntry(String name, String cert, String key, String chain, String password) { + this.name = name; + this.cert = cert; + this.key = key; + this.chain = chain; + this.password = password; + } + + public void setName(String name) { + this.name = name; + } + public String getName() { + return name; + } + public void setCert(String cert) { + this.cert = cert; + } + public String getCert() { + return cert; + } + public void setKey(String key) { + this.key = key; + } + public String getKey() { + return key; + } + public void setChain(String chain) { + this.chain = chain; + } + public String getChain() { + return chain; + } + public void setPassword(String password) { + this.password = password; + } + public String getPassword() { + return password; + } + } + public LoadBalancerRule() { // Empty constructor for (de)serialization } @@ -101,4 +149,12 @@ public class LoadBalancerRule { public void setRouterIp(final String routerIp) { this.routerIp = routerIp; } + + public SslCertEntry[] getSslCerts() { + return sslCerts; + } + + public void setSslCerts(final SslCertEntry[] sslCerts) { + this.sslCerts = sslCerts; + } } diff --git a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java index 7229f0903b7..eabcc82040d 100644 --- a/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java +++ b/core/src/main/java/com/cloud/agent/transport/ArrayTypeAdaptor.java @@ -75,13 +75,17 @@ public class ArrayTypeAdaptor implements JsonDeserializer, JsonSerialize try { clazz = Class.forName(name); } catch (ClassNotFoundException e) { - throw new CloudRuntimeException("can't find " + name); + throw new JsonParseException("can't find " + name); } T cmd = (T)_gson.fromJson(entry.getValue(), clazz); cmds.add(cmd); } - Class type = ((Class)typeOfT).getComponentType(); - T[] ts = (T[])Array.newInstance(type, cmds.size()); - return cmds.toArray(ts); + try { + Class type = Class.forName(typeOfT.getTypeName().replace("[]", "")); + T[] ts = (T[])Array.newInstance(type, cmds.size()); + return cmds.toArray(ts); + } catch (ClassNotFoundException e) { + throw new CloudRuntimeException("can't find " + typeOfT.getTypeName()); + } } } diff --git a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java index e4b0a7ffff4..7736bea3cda 100644 --- a/core/src/main/java/com/cloud/network/HAProxyConfigurator.java +++ b/core/src/main/java/com/cloud/network/HAProxyConfigurator.java @@ -36,6 +36,8 @@ import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.agent.api.to.LoadBalancerTO.DestinationTO; import com.cloud.agent.api.to.LoadBalancerTO.StickinessPolicyTO; import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.rules.LbStickinessMethod.StickinessMethodType; import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; @@ -52,6 +54,12 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { private static String[] defaultListen = {"listen vmops", "\tbind 0.0.0.0:9", "\toption transparent"}; + private static final String SSL_CERTS_DIR = "/etc/cloudstack/ssl/"; + + private static final String SSL_CONFIGURATION_INTERMEDIATE = " ssl-min-ver TLSv1.2 no-tls-tickets " + + "ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-GCM-SHA256 " + + "ciphersuites TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"; + @Override public String[] generateConfiguration(final List fwRules) { // Group the rules by publicip:publicport @@ -469,30 +477,41 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { return sb.toString(); } - private List getRulesForPool(final LoadBalancerTO lbTO, final boolean keepAliveEnabled) { + private List getRulesForPool(final LoadBalancerTO lbTO, final LoadBalancerConfigCommand lbCmd) { StringBuilder sb = new StringBuilder(); final String poolName = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString(); final String publicIP = lbTO.getSrcIp(); final int publicPort = lbTO.getSrcPort(); final String algorithm = lbTO.getAlgorithm(); - final List result = new ArrayList(); - // add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80" - sb = new StringBuilder(); - sb.append("listen ").append(poolName); - result.add(sb.toString()); + boolean sslOffloading = lbTO.getSslCert() != null && !lbTO.getSslCert().isRevoked() + && NetUtils.SSL_PROTO.equals(lbTO.getLbProtocol()); + + final List frontendConfigs = new ArrayList<>(); + final List backendConfigs = new ArrayList<>(); + final List result = new ArrayList<>(); + sb = new StringBuilder(); sb.append("\tbind ").append(publicIP).append(":").append(publicPort); - result.add(sb.toString()); + + if (sslOffloading) { + sb.append(" ssl crt ").append(SSL_CERTS_DIR).append(poolName).append(".pem"); + // check for http2 support + sb.append(" alpn h2,http/1.1"); + sb.append(SSL_CONFIGURATION_INTERMEDIATE); + sb.append("\n\thttp-request add-header X-Forwarded-Proto https"); + } + frontendConfigs.add(sb.toString()); + sb = new StringBuilder(); sb.append("\t").append("balance ").append(algorithm.toLowerCase()); - result.add(sb.toString()); + backendConfigs.add(sb.toString()); int i = 0; - Boolean destsAvailable = false; + boolean destsAvailable = false; final String stickinessSubRule = getLbSubRuleForStickiness(lbTO); - final List dstSubRule = new ArrayList(); - final List dstWithCookieSubRule = new ArrayList(); + final List dstSubRule = new ArrayList<>(); + final List dstWithCookieSubRule = new ArrayList<>(); for (final DestinationTO dest : lbTO.getDestinations()) { // add line like this: "server 65_37_141_30-80_3 10.1.1.4:80 check" if (dest.isRevoked()) { @@ -500,15 +519,20 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { } sb = new StringBuilder(); sb.append("\t") - .append("server ") - .append(poolName) - .append("_") - .append(Integer.toString(i++)) - .append(" ") - .append(dest.getDestIp()) - .append(":") - .append(dest.getDestPort()) - .append(" check"); + .append("server ") + .append(poolName) + .append("_") + .append(i++) + .append(" ") + .append(dest.getDestIp()) + .append(":") + .append(dest.getDestPort()) + .append(" check"); + + if (sslOffloading) { + sb.append(SSL_CONFIGURATION_INTERMEDIATE); + } + if(lbTO.getLbProtocol() != null && lbTO.getLbProtocol().equals("tcp-proxy")) { sb.append(" send-proxy"); } @@ -520,9 +544,9 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { destsAvailable = true; } - Boolean httpbasedStickiness = false; + boolean httpbasedStickiness = false; /* attach stickiness sub rule only if the destinations are available */ - if (stickinessSubRule != null && destsAvailable == true) { + if (stickinessSubRule != null && destsAvailable) { for (final StickinessPolicyTO stickinessPolicy : lbTO.getStickinessPolicies()) { if (stickinessPolicy == null) { continue; @@ -530,35 +554,40 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) || StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName())) { httpbasedStickiness = true; + break; } } if (httpbasedStickiness) { - result.addAll(dstWithCookieSubRule); + backendConfigs.addAll(dstWithCookieSubRule); } else { - result.addAll(dstSubRule); + backendConfigs.addAll(dstSubRule); } - result.add(stickinessSubRule); + backendConfigs.add(stickinessSubRule); } else { - result.addAll(dstSubRule); + backendConfigs.addAll(dstSubRule); } if (stickinessSubRule != null && !destsAvailable) { logger.warn("Haproxy stickiness policy for lb rule: " + lbTO.getSrcIp() + ":" + lbTO.getSrcPort() + ": Not Applied, cause: backends are unavailable"); } - if (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled || httpbasedStickiness) { - sb = new StringBuilder(); - sb.append("\t").append("mode http"); - result.add(sb.toString()); - sb = new StringBuilder(); - sb.append("\t").append("option httpclose"); - result.add(sb.toString()); + boolean keepAliveEnabled = lbCmd.keepAliveEnabled; + boolean http = (publicPort == NetUtils.HTTP_PORT && !keepAliveEnabled); + if (http || httpbasedStickiness || sslOffloading) { + frontendConfigs.add("\tmode http"); + String keepAliveLine = keepAliveEnabled ? "\tno option forceclose" : "\toption httpclose"; + frontendConfigs.add(keepAliveLine); } + // add line like this: "listen 65_37_141_30-80\n\tbind 65.37.141.30:80" + result.add(String.format("listen %s", poolName)); + result.addAll(frontendConfigs); + String cidrList = lbTO.getCidrList(); if (StringUtils.isNotBlank(cidrList)) { result.add(String.format("\tacl network_allowed src %s \n\ttcp-request connection reject if !network_allowed", cidrList)); } + result.addAll(backendConfigs); result.add(blankLine); return result; } @@ -566,15 +595,18 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { private String generateStatsRule(final LoadBalancerConfigCommand lbCmd, final String ruleName, final String statsIp) { final StringBuilder rule = new StringBuilder("\nlisten ").append(ruleName).append("\n\tbind ").append(statsIp).append(":").append(lbCmd.lbStatsPort); // TODO DH: write test for this in both cases - if (!lbCmd.keepAliveEnabled) { - logger.info("Haproxy mode http enabled"); - rule.append("\n\tmode http\n\toption httpclose"); + rule.append("\n\tmode http"); + if (lbCmd.keepAliveEnabled) { + logger.info("Haproxy option http-keep-alive enabled"); + } else { + logger.info("Haproxy option httpclose enabled"); + rule.append("\n\toption httpclose"); } rule.append("\n\tstats enable\n\tstats uri ") - .append(lbCmd.lbStatsUri) - .append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ") - .append(lbCmd.lbStatsAuth); - rule.append("\n"); + .append(lbCmd.lbStatsUri) + .append("\n\tstats realm Haproxy\\ Statistics\n\tstats auth ") + .append(lbCmd.lbStatsAuth) + .append("\n"); final String result = rule.toString(); if (logger.isDebugEnabled()) { logger.debug("Haproxystats rule: " + result); @@ -644,7 +676,7 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { if (lbTO.isRevoked()) { continue; } - final List poolRules = getRulesForPool(lbTO, lbCmd.keepAliveEnabled); + final List poolRules = getRulesForPool(lbTO, lbCmd); result.addAll(poolRules); has_listener = true; } @@ -696,4 +728,30 @@ public class HAProxyConfigurator implements LoadBalancerConfigurator { return result; } + + @Override + public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd) { + final Set sslCertEntries = new HashSet<>(); + for (final LoadBalancerTO lbTO : lbCmd.getLoadBalancers()) { + if (lbTO.getSslCert() != null) { + addSslCertEntry(sslCertEntries, lbTO); + } + } + final SslCertEntry[] result = sslCertEntries.toArray(new SslCertEntry[sslCertEntries.size()]); + return result; + } + + private void addSslCertEntry(Set sslCertEntries, LoadBalancerTO lbTO) { + final LbSslCert cert = lbTO.getSslCert(); + if (cert.isRevoked()) { + return; + } + if (lbTO.getLbProtocol() == null || ! lbTO.getLbProtocol().equals(NetUtils.SSL_PROTO)) { + return; + } + StringBuilder sb = new StringBuilder(); + final String name = sb.append(lbTO.getSrcIp().replace(".", "_")).append('-').append(lbTO.getSrcPort()).toString(); + final SslCertEntry sslCertEntry = new SslCertEntry(name, cert.getCert(), cert.getKey(), cert.getChain(), cert.getPassword()); + sslCertEntries.add(sslCertEntry); + } } diff --git a/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java b/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java index 0e19b1e606e..8814f60b071 100644 --- a/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java +++ b/core/src/main/java/com/cloud/network/LoadBalancerConfigurator.java @@ -23,6 +23,7 @@ import java.util.List; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule.SslCertEntry; public interface LoadBalancerConfigurator { public final static int ADD = 0; @@ -34,4 +35,6 @@ public interface LoadBalancerConfigurator { public String[] generateConfiguration(LoadBalancerConfigCommand lbCmd); public String[][] generateFwRules(LoadBalancerConfigCommand lbCmd); + + public SslCertEntry[] generateSslCertEntries(LoadBalancerConfigCommand lbCmd); } diff --git a/core/src/main/java/com/cloud/resource/ServerResource.java b/core/src/main/java/com/cloud/resource/ServerResource.java index 845ac8a48fa..23d200942a2 100644 --- a/core/src/main/java/com/cloud/resource/ServerResource.java +++ b/core/src/main/java/com/cloud/resource/ServerResource.java @@ -22,6 +22,7 @@ package com.cloud.resource; import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingAnswer; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.host.Host; @@ -90,4 +91,5 @@ public interface ServerResource extends Manager { return false; } + default void processPingAnswer(PingAnswer answer) {}; } diff --git a/core/src/main/java/com/cloud/serializer/GsonHelper.java b/core/src/main/java/com/cloud/serializer/GsonHelper.java index 2d2cecf2618..7de98c08b7e 100644 --- a/core/src/main/java/com/cloud/serializer/GsonHelper.java +++ b/core/src/main/java/com/cloud/serializer/GsonHelper.java @@ -62,7 +62,7 @@ public class GsonHelper { LOGGER.info("Default Builder inited."); } - static Gson setDefaultGsonConfig(GsonBuilder builder) { + public static Gson setDefaultGsonConfig(GsonBuilder builder) { builder.setVersion(1.5); InterfaceTypeAdaptor dsAdaptor = new InterfaceTypeAdaptor(); builder.registerTypeAdapter(DataStoreTO.class, dsAdaptor); diff --git a/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java b/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java index 7d8225462ca..58885210120 100644 --- a/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java +++ b/core/src/main/java/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java @@ -142,7 +142,7 @@ public class StorageSubsystemCommandHandlerBase implements StorageSubsystemComma } return new CreateObjectAnswer("not supported type"); } catch (Exception e) { - logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); + logger.error("Failed to create object [{}] due to [{}].", data.getObjectType(), e.getMessage(), e); return new CreateObjectAnswer(e.toString()); } } diff --git a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java index 9b126846827..4c056f256cf 100755 --- a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java @@ -27,6 +27,7 @@ import java.io.InputStream; import java.io.RandomAccessFile; import java.net.URI; import java.net.URISyntaxException; +import java.util.Arrays; import java.util.Date; import java.util.List; @@ -80,6 +81,18 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te private ResourceType resourceType = ResourceType.TEMPLATE; private final HttpMethodRetryHandler myretryhandler; private boolean followRedirects = false; + private boolean isChunkedTransfer; + + protected static final List CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE = Arrays.asList( + "x-goog-stored-content-length", + "x-goog-meta-size", + "x-amz-meta-size", + "x-amz-meta-content-length", + "x-object-meta-size", + "x-original-content-length", + "x-oss-meta-content-length", + "x-file-size"); + private static final long MIN_FORMAT_VERIFICATION_SIZE = 1024 * 1024; public HttpTemplateDownloader(StorageLayer storageLayer, String downloadUrl, String toDir, DownloadCompleteCallback callback, long maxTemplateSizeInBytes, String user, String password, Proxy proxy, ResourceType resourceType) { @@ -205,13 +218,11 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te RandomAccessFile out = new RandomAccessFile(file, "rw"); ) { out.seek(localFileSize); - - logger.info("Starting download from " + downloadUrl + " to " + toFile + " remoteSize=" + toHumanReadableSize(remoteSize) + " , max size=" + toHumanReadableSize(maxTemplateSizeInBytes)); - - if (copyBytes(file, in, out)) return 0; - + logger.info("Starting download from {} to {} remoteSize={} , max size={}",downloadUrl, toFile, + toHumanReadableSize(remoteSize), toHumanReadableSize(maxTemplateSizeInBytes)); + boolean eof = copyBytes(file, in, out); Date finish = new Date(); - checkDowloadCompletion(); + checkDownloadCompletion(eof); downloadTime += finish.getTime() - start.getTime(); } finally { /* in.close() and out.close() */ } return totalBytes; @@ -237,28 +248,32 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te } private boolean copyBytes(File file, InputStream in, RandomAccessFile out) throws IOException { - int bytes; - byte[] block = new byte[CHUNK_SIZE]; + byte[] buffer = new byte[CHUNK_SIZE]; long offset = 0; - boolean done = false; VerifyFormat verifyFormat = new VerifyFormat(file); status = Status.IN_PROGRESS; - while (!done && status != Status.ABORTED && offset <= remoteSize) { - if ((bytes = in.read(block, 0, CHUNK_SIZE)) > -1) { - offset = writeBlock(bytes, out, block, offset); - if (!ResourceType.SNAPSHOT.equals(resourceType) && - !verifyFormat.isVerifiedFormat() && - (offset >= 1048576 || offset >= remoteSize)) { //let's check format after we get 1MB or full file - verifyFormat.invoke(); - } - } else { - done = true; + while (status != Status.ABORTED) { + int bytesRead = in.read(buffer, 0, CHUNK_SIZE); + if (bytesRead == -1) { + logger.debug("Reached EOF on input stream"); + break; + } + offset = writeBlock(bytesRead, out, buffer, offset); + if (!ResourceType.SNAPSHOT.equals(resourceType) + && !verifyFormat.isVerifiedFormat() + && (offset >= MIN_FORMAT_VERIFICATION_SIZE || offset >= remoteSize)) { + verifyFormat.invoke(); + } + if (offset >= remoteSize) { + logger.debug("Reached expected remote size limit: {} bytes", remoteSize); + break; } } out.getFD().sync(); - return false; + return !Status.ABORTED.equals(status); } + private long writeBlock(int bytes, RandomAccessFile out, byte[] block, long offset) throws IOException { out.write(block, 0, bytes); offset += bytes; @@ -267,11 +282,13 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te return offset; } - private void checkDowloadCompletion() { + private void checkDownloadCompletion(boolean eof) { String downloaded = "(incomplete download)"; - if (totalBytes >= remoteSize) { + if (eof && ((totalBytes >= remoteSize) || (isChunkedTransfer && remoteSize == maxTemplateSizeInBytes))) { status = Status.DOWNLOAD_FINISHED; - downloaded = "(download complete remote=" + toHumanReadableSize(remoteSize) + " bytes)"; + downloaded = "(download complete remote=" + + (remoteSize == maxTemplateSizeInBytes ? toHumanReadableSize(remoteSize) : "unknown") + + " bytes)"; } errorString = "Downloaded " + toHumanReadableSize(totalBytes) + " bytes " + downloaded; } @@ -293,18 +310,42 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te } } + protected long getRemoteSizeForChunkedTransfer() { + for (String headerKey : CUSTOM_HEADERS_FOR_CHUNKED_TRANSFER_SIZE) { + Header header = request.getResponseHeader(headerKey); + if (header == null) { + continue; + } + try { + return Long.parseLong(header.getValue()); + } catch (NumberFormatException ignored) {} + } + Header contentRangeHeader = request.getResponseHeader("Content-Range"); + if (contentRangeHeader != null) { + String contentRange = contentRangeHeader.getValue(); + if (contentRange != null && contentRange.contains("/")) { + String totalSize = contentRange.substring(contentRange.indexOf('/') + 1).trim(); + return Long.parseLong(totalSize); + } + } + return 0; + } + private boolean tryAndGetRemoteSize() { Header contentLengthHeader = request.getResponseHeader("content-length"); - boolean chunked = false; + isChunkedTransfer = false; long reportedRemoteSize = 0; if (contentLengthHeader == null) { Header chunkedHeader = request.getResponseHeader("Transfer-Encoding"); - if (chunkedHeader == null || !"chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + if (chunkedHeader != null && "chunked".equalsIgnoreCase(chunkedHeader.getValue())) { + isChunkedTransfer = true; + reportedRemoteSize = getRemoteSizeForChunkedTransfer(); + logger.debug("{} is using chunked transfer encoding, possible remote size: {}", downloadUrl, + reportedRemoteSize); + } else { status = Status.UNRECOVERABLE_ERROR; errorString = " Failed to receive length of download "; return false; - } else if ("chunked".equalsIgnoreCase(chunkedHeader.getValue())) { - chunked = true; } } else { reportedRemoteSize = Long.parseLong(contentLengthHeader.getValue()); @@ -316,9 +357,11 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te return false; } } - if (remoteSize == 0) { remoteSize = reportedRemoteSize; + if (remoteSize != 0) { + logger.debug("Remote size for {} found to be {}", downloadUrl, toHumanReadableSize(remoteSize)); + } } return true; } diff --git a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java index d6622bed73e..349997da1b7 100644 --- a/core/src/main/java/com/cloud/storage/template/TemplateConstants.java +++ b/core/src/main/java/com/cloud/storage/template/TemplateConstants.java @@ -24,7 +24,7 @@ public final class TemplateConstants { public static final String DEFAULT_SNAPSHOT_ROOT_DIR = "snapshots"; public static final String DEFAULT_VOLUME_ROOT_DIR = "volumes"; public static final String DEFAULT_TMPLT_FIRST_LEVEL_DIR = "tmpl/"; - + public static final String DEFAULT_CHECKPOINT_ROOT_DIR = "checkpoints"; public static final String DEFAULT_SYSTEM_VM_TEMPLATE_PATH = "template/tmpl/1/"; public static final int DEFAULT_TMPLT_COPY_PORT = 80; diff --git a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java index 50cf956c9e7..864a3e22eb3 100644 --- a/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java +++ b/core/src/main/java/org/apache/cloudstack/agent/lb/SetupMSListCommand.java @@ -26,20 +26,28 @@ import com.cloud.agent.api.Command; public class SetupMSListCommand extends Command { private List msList; + private List avoidMsList; private String lbAlgorithm; private Long lbCheckInterval; + private Boolean triggerHostLb; - public SetupMSListCommand(final List msList, final String lbAlgorithm, final Long lbCheckInterval) { + public SetupMSListCommand(final List msList, final List avoidMsList, final String lbAlgorithm, final Long lbCheckInterval, final Boolean triggerHostLb) { super(); this.msList = msList; + this.avoidMsList = avoidMsList; this.lbAlgorithm = lbAlgorithm; this.lbCheckInterval = lbCheckInterval; + this.triggerHostLb = triggerHostLb; } public List getMsList() { return msList; } + public List getAvoidMsList() { + return avoidMsList; + } + public String getLbAlgorithm() { return lbAlgorithm; } @@ -48,9 +56,12 @@ public class SetupMSListCommand extends Command { return lbCheckInterval; } + public boolean getTriggerHostLb() { + return triggerHostLb; + } + @Override public boolean executeInSequence() { return false; } - } diff --git a/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java index 09f9c562150..ffc67b628a7 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupAnswer.java @@ -28,6 +28,7 @@ public class BackupAnswer extends Answer { private Long size; private Long virtualSize; private Map volumes; + Boolean needsCleanup; public BackupAnswer(final Command command, final boolean success, final String details) { super(command, success, details); @@ -56,4 +57,15 @@ public class BackupAnswer extends Answer { public void setVolumes(Map volumes) { this.volumes = volumes; } + + public Boolean getNeedsCleanup() { + if (needsCleanup == null) { + return false; + } + return needsCleanup; + } + + public void setNeedsCleanup(Boolean needsCleanup) { + this.needsCleanup = needsCleanup; + } } diff --git a/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java b/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java new file mode 100644 index 00000000000..eabf6877ba6 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/BackupStorageStatsAnswer.java @@ -0,0 +1,50 @@ +// +// 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.backup; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class BackupStorageStatsAnswer extends Answer { + private Long totalSize; + private Long usedSize; + + public BackupStorageStatsAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + this.totalSize = 0L; + this.usedSize = 0L; + } + + public Long getTotalSize() { + return totalSize; + } + + public void setTotalSize(Long totalSize) { + this.totalSize = totalSize; + } + + public Long getUsedSize() { + return usedSize; + } + + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java b/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java new file mode 100644 index 00000000000..1ceeac17e52 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/backup/GetBackupStorageStatsCommand.java @@ -0,0 +1,66 @@ +// +// 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.backup; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.LogLevel; + +public class GetBackupStorageStatsCommand extends Command { + private String backupRepoType; + private String backupRepoAddress; + @LogLevel(LogLevel.Log4jLevel.Off) + private String mountOptions; + + public GetBackupStorageStatsCommand(String backupRepoType, String backupRepoAddress, String mountOptions) { + super(); + this.backupRepoType = backupRepoType; + this.backupRepoAddress = backupRepoAddress; + this.mountOptions = mountOptions; + } + + public String getBackupRepoType() { + return backupRepoType; + } + + public void setBackupRepoType(String backupRepoType) { + this.backupRepoType = backupRepoType; + } + + public String getBackupRepoAddress() { + return backupRepoAddress; + } + + public void setBackupRepoAddress(String backupRepoAddress) { + this.backupRepoAddress = backupRepoAddress; + } + + public String getMountOptions() { + return mountOptions == null ? "" : mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + + @Override + public boolean executeInSequence() { + return true; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java index 7228e35147a..f447fbe3d00 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/RestoreBackupCommand.java @@ -30,7 +30,8 @@ public class RestoreBackupCommand extends Command { private String backupPath; private String backupRepoType; private String backupRepoAddress; - private List volumePaths; + private List backupVolumesUUIDs; + private List restoreVolumePaths; private String diskType; private Boolean vmExists; private String restoreVolumeUUID; @@ -72,12 +73,12 @@ public class RestoreBackupCommand extends Command { this.backupRepoAddress = backupRepoAddress; } - public List getVolumePaths() { - return volumePaths; + public List getRestoreVolumePaths() { + return restoreVolumePaths; } - public void setVolumePaths(List volumePaths) { - this.volumePaths = volumePaths; + public void setRestoreVolumePaths(List restoreVolumePaths) { + this.restoreVolumePaths = restoreVolumePaths; } public Boolean isVmExists() { @@ -127,4 +128,12 @@ public class RestoreBackupCommand extends Command { public boolean executeInSequence() { return true; } + + public List getBackupVolumesUUIDs() { + return backupVolumesUUIDs; + } + + public void setBackupVolumesUUIDs(List backupVolumesUUIDs) { + this.backupVolumesUUIDs = backupVolumesUUIDs; + } } diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java index 93855ea1721..ecebd57a178 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java @@ -30,6 +30,7 @@ public class TakeBackupCommand extends Command { private String backupRepoType; private String backupRepoAddress; private List volumePaths; + private Boolean quiesce; @LogLevel(LogLevel.Log4jLevel.Off) private String mountOptions; @@ -87,6 +88,14 @@ public class TakeBackupCommand extends Command { this.volumePaths = volumePaths; } + public Boolean getQuiesce() { + return quiesce; + } + + public void setQuiesce(Boolean quiesce) { + this.quiesce = quiesce; + } + @Override public boolean executeInSequence() { return true; diff --git a/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java b/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java new file mode 100644 index 00000000000..b9bb702e345 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/CommandInfo.java @@ -0,0 +1,124 @@ +// 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.command; + +import com.cloud.agent.api.Command; +import com.cloud.serializer.GsonHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.Date; + +public class CommandInfo { + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZ"; + public static final Gson GSON = GsonHelper.setDefaultGsonConfig(new GsonBuilder().setDateFormat(DATE_FORMAT)); + + long requestSeq; + Command.State state; + Date startTime; + Date updateTime; + String commandName; + String command; + int timeout; + String answerName; + String answer; + + public CommandInfo() { + } + + public CommandInfo(long requestSeq, Command command, Command.State state) { + this.requestSeq = requestSeq; + this.state = state; + this.startTime = this.updateTime = new Date(); + this.commandName = command.getClass().getName(); + this.command = GSON.toJson(command); + this.timeout = command.getWait(); + } + + public long getRequestSeq() { + return requestSeq; + } + + public void setRequestSeq(long requestSeq) { + this.requestSeq = requestSeq; + } + + public Command.State getState() { + return state; + } + + public void setState(Command.State state) { + this.state = state; + this.updateTime = new Date(); + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getAnswerName() { + return answerName; + } + + public void setAnswerName(String answerName) { + this.answerName = answerName; + } + + public String getAnswer() { + return answer; + } + + public void setAnswer(String answer) { + this.answer = answer; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java new file mode 100644 index 00000000000..e8d27e1fa71 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileAnswer.java @@ -0,0 +1,45 @@ +// +// 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.command; + +import com.cloud.agent.api.Answer; +import org.apache.cloudstack.api.ApiCommandResourceType; + +public class ReconcileAnswer extends Answer { + + ApiCommandResourceType resourceType; + Long resourceId; + + public ApiCommandResourceType getResourceType() { + return resourceType; + } + + public void setResourceType(ApiCommandResourceType resourceType) { + this.resourceType = resourceType; + } + + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(Long resourceId) { + this.resourceId = resourceId; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java new file mode 100644 index 00000000000..262aefb30ac --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommand.java @@ -0,0 +1,33 @@ +// 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.command; + +import com.cloud.agent.api.Command; + +public class ReconcileCommand extends Command { + + @Override + public boolean executeInSequence() { + return false; + } + + @Override + public int getWait() { + return 30; // timeout is 30 seconds + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java new file mode 100644 index 00000000000..8acc02a730f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCommandUtils.java @@ -0,0 +1,192 @@ +// 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.command; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.JsonSyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; + +public class ReconcileCommandUtils { + + protected static final Logger LOGGER = LogManager.getLogger(ReconcileCommandUtils.class.getName()); + + public static void createLogFileForCommand(final String logPath, final Command cmd) { + updateLogFileForCommand(logPath, cmd, Command.State.CREATED); + } + + public static void updateLogFileForCommand(final String logPath, final Command cmd, final Command.State state) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with %s state", logFileName, state)); + File logFile = new File(logFileName); + CommandInfo commandInfo = null; + if (logFile.exists()) { + commandInfo = readLogFileForCommand(logFileName); + logFile.delete(); + } + if (commandInfo == null) { + commandInfo = new CommandInfo(cmd.getRequestSequence(), cmd, state); + } else { + commandInfo.setState(state); + } + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } + + public static void updateLogFileForCommand(final String logFullPath, final Command.State state) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Updating log file %s with %s state", logFile.getName(), state)); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFullPath); + if (commandInfo != null) { + commandInfo.setState(state); + } + logFile.delete(); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + + public static void deleteLogFileForCommand(final String logPath, final Command cmd) { + if (cmd.isReconcile()) { + File logFile = new File(getLogFileNameForCommand(logPath, cmd)); + LOGGER.debug(String.format("Removing log file %s", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + } + + public static void deleteLogFile(final String logFullPath) { + File logFile = new File(logFullPath); + LOGGER.debug(String.format("Removing log file %s ", logFile.getName())); + if (logFile.exists()) { + logFile.delete(); + } + } + + public static String getLogFileNameForCommand(final String logPath, final Command cmd) { + return String.format("%s/%s-%s.json", logPath, cmd.getRequestSequence(), cmd); + } + + public static CommandInfo readLogFileForCommand(final String logFullPath) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleDateFormat df = new SimpleDateFormat(CommandInfo.DATE_FORMAT); + objectMapper.setDateFormat(df); + return objectMapper.readValue(new File(logFullPath), CommandInfo.class); + } catch (IOException e) { + LOGGER.error(String.format("Failed to read log file %s: %s", logFullPath, e.getMessage())); + return null; + } + } + + public static Command parseCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getCommandName() == null || commandInfo.getCommand() == null) { + return null; + } + return parseCommandInfo(commandInfo.getCommandName(), commandInfo.getCommand()); + } + + public static Command parseCommandInfo(final String commandName, final String commandInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(commandName); + parsedObject = CommandInfo.GSON.fromJson(commandInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse command from CommandInfo %s due to %s", commandInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Command) parsedObject; + } + return null; + } + + public static Answer parseAnswerFromCommandInfo(final CommandInfo commandInfo) { + if (commandInfo.getAnswerName() == null || commandInfo.getAnswer() == null) { + return null; + } + return parseAnswerFromAnswerInfo(commandInfo.getAnswerName(), commandInfo.getAnswer()); + } + + public static Answer parseAnswerFromAnswerInfo(final String answerName, final String answerInfo) { + Object parsedObject = null; + try { + Class commandClazz = Class.forName(answerName); + parsedObject = CommandInfo.GSON.fromJson(answerInfo, commandClazz); + } catch (ClassNotFoundException | JsonSyntaxException e) { + LOGGER.error(String.format("Failed to parse answer from answerInfo %s due to %s", answerInfo, e.getMessage())); + } + if (parsedObject != null) { + return (Answer) parsedObject; + } + return null; + } + + public static void updateLogFileWithAnswerForCommand(final String logPath, final Command cmd, final Answer answer) { + if (cmd.isReconcile()) { + String logFileName = getLogFileNameForCommand(logPath, cmd); + LOGGER.debug(String.format("Updating log file %s with answer %s", logFileName, answer)); + File logFile = new File(logFileName); + if (!logFile.exists()) { + return; + } + CommandInfo commandInfo = readLogFileForCommand(logFile.getAbsolutePath()); + if (commandInfo == null) { + return; + } + if (Command.State.STARTED.equals(commandInfo.getState())) { + if (answer.getResult()) { + commandInfo.setState(Command.State.COMPLETED); + } else { + commandInfo.setState(Command.State.FAILED); + } + } + commandInfo.setAnswerName(answer.toString()); + commandInfo.setAnswer(CommandInfo.GSON.toJson(answer)); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(logFile)); + writer.write(CommandInfo.GSON.toJson(commandInfo)); + writer.close(); + } catch (IOException e) { + LOGGER.error(String.format("Failed to write log file %s", logFile)); + } + } + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java new file mode 100644 index 00000000000..82a24fa7fa5 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyAnswer.java @@ -0,0 +1,56 @@ +// +// 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.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileCopyAnswer extends ReconcileVolumeAnswer { + + boolean isSkipped = false; + String reason; + + public ReconcileCopyAnswer(boolean isSkipped, String reason) { + super(); + this.isSkipped = isSkipped; + this.reason = reason; + } + + public ReconcileCopyAnswer(boolean isSkipped, boolean result, String reason) { + super(); + this.isSkipped = isSkipped; + this.result = result; + this.reason = reason; + } + + public ReconcileCopyAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.isSkipped = false; + this.result = true; + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public boolean isSkipped() { + return isSkipped; + } + + public String getReason() { + return reason; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java new file mode 100644 index 00000000000..36a678833d0 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileCopyCommand.java @@ -0,0 +1,53 @@ +// 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.command; + +import com.cloud.agent.api.to.DataTO; + +import java.util.Map; + +public class ReconcileCopyCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + Map option; // details of source volume + Map option2; // details of destination volume + + public ReconcileCopyCommand(DataTO srcData, DataTO destData, Map option, Map option2) { + this.srcData = srcData; + this.destData = destData; + this.option = option; + this.option2 = option2; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public Map getOption() { + return option; + } + + public Map getOption2() { + return option2; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java new file mode 100644 index 00000000000..6313267c7e4 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateAnswer.java @@ -0,0 +1,68 @@ +// +// 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.command; + +import com.cloud.vm.VirtualMachine; + +import java.util.List; + +public class ReconcileMigrateAnswer extends ReconcileAnswer { + + Long hostId; + String vmName; + VirtualMachine.State vmState; + List vmDisks; + + public ReconcileMigrateAnswer() { + } + + public ReconcileMigrateAnswer(String vmName, VirtualMachine.State vmState) { + this.vmName = vmName; + this.vmState = vmState; + } + + public Long getHostId() { + return hostId; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public String getVmName() { + return vmName; + } + + public VirtualMachine.State getVmState() { + return vmState; + } + + public void setVmState(VirtualMachine.State vmState) { + this.vmState = vmState; + } + + public List getVmDisks() { + return vmDisks; + } + + public void setVmDisks(List vmDisks) { + this.vmDisks = vmDisks; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java new file mode 100644 index 00000000000..50e1c68a65f --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateCommand.java @@ -0,0 +1,31 @@ +// 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.command; + +public class ReconcileMigrateCommand extends ReconcileCommand { + + String vmName; + + public ReconcileMigrateCommand(String vmName) { + this.vmName = vmName; + } + + public String getVmName() { + return vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java new file mode 100644 index 00000000000..ebbd913f971 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeAnswer.java @@ -0,0 +1,50 @@ +// +// 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.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +import java.util.List; + +public class ReconcileMigrateVolumeAnswer extends ReconcileVolumeAnswer { + + String vmName; + List vmDiskPaths; + + public ReconcileMigrateVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + super(volumeOnSource, volumeOnDestination); + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public List getVmDiskPaths() { + return vmDiskPaths; + } + + public void setVmDiskPaths(List vmDiskPaths) { + this.vmDiskPaths = vmDiskPaths; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java new file mode 100644 index 00000000000..e3e75249406 --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileMigrateVolumeCommand.java @@ -0,0 +1,48 @@ +// 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.command; + +import com.cloud.agent.api.to.DataTO; + +public class ReconcileMigrateVolumeCommand extends ReconcileCommand { + + DataTO srcData; + DataTO destData; + String vmName; + + public ReconcileMigrateVolumeCommand(DataTO srcData, DataTO destData) { + this.srcData = srcData; + this.destData = destData; + } + + public DataTO getSrcData() { + return srcData; + } + + public DataTO getDestData() { + return destData; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java new file mode 100644 index 00000000000..da10bf23aee --- /dev/null +++ b/core/src/main/java/org/apache/cloudstack/command/ReconcileVolumeAnswer.java @@ -0,0 +1,46 @@ +// +// 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.command; + +import org.apache.cloudstack.storage.volume.VolumeOnStorageTO; + +public class ReconcileVolumeAnswer extends ReconcileAnswer { + + // (1) null object: volume is not available. For example the source is secondary storage + // (2) otherwise, if volumeOnSource.getPath() is null, the volume cannot be found on primary storage pool + VolumeOnStorageTO volumeOnSource; + VolumeOnStorageTO volumeOnDestination; + + public ReconcileVolumeAnswer() { + } + + public ReconcileVolumeAnswer(VolumeOnStorageTO volumeOnSource, VolumeOnStorageTO volumeOnDestination) { + this.volumeOnSource = volumeOnSource; + this.volumeOnDestination = volumeOnDestination; + } + + public VolumeOnStorageTO getVolumeOnSource() { + return volumeOnSource; + } + + public VolumeOnStorageTO getVolumeOnDestination() { + return volumeOnDestination; + } +} diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java index b8a25a11b5c..b84718e4f93 100644 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java +++ b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsDirectTemplateDownloader.java @@ -39,9 +39,7 @@ import java.util.Map; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import org.apache.cloudstack.utils.security.SSLUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.io.IOUtils; @@ -55,6 +53,7 @@ import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import com.cloud.utils.Pair; @@ -98,8 +97,8 @@ public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl req = new HttpGet(downloadUrl); setFollowRedirects(this.isFollowRedirects()); if (MapUtils.isNotEmpty(headers)) { - for (String headerKey: headers.keySet()) { - req.setHeader(headerKey, headers.get(headerKey)); + for (Map.Entry entry : headers.entrySet()) { + req.setHeader(entry.getKey(), entry.getValue()); } } } @@ -120,10 +119,10 @@ public class HttpsDirectTemplateDownloader extends DirectTemplateDownloaderImpl String password = "changeit"; defaultKeystore.load(is, password.toCharArray()); } - TrustManager[] tm = HttpsMultiTrustManager.getTrustManagersFromKeyStores(customKeystore, defaultKeystore); - SSLContext sslContext = SSLUtils.getSSLContext(); - sslContext.init(null, tm, null); - return sslContext; + return SSLContexts.custom() + .loadTrustMaterial(customKeystore, null) + .loadTrustMaterial(defaultKeystore, null) + .build(); } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | KeyManagementException e) { logger.error(String.format("Failure getting SSL context for HTTPS downloader, using default SSL context: %s", e.getMessage()), e); try { diff --git a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java b/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java deleted file mode 100644 index fe47847c36c..00000000000 --- a/core/src/main/java/org/apache/cloudstack/direct/download/HttpsMultiTrustManager.java +++ /dev/null @@ -1,102 +0,0 @@ -// 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.direct.download; - -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; - -public class HttpsMultiTrustManager implements X509TrustManager { - - private final List trustManagers; - - public HttpsMultiTrustManager(KeyStore... keystores) { - List trustManagers = new ArrayList<>(); - trustManagers.add(getTrustManager(null)); - for (KeyStore keystore : keystores) { - trustManagers.add(getTrustManager(keystore)); - } - this.trustManagers = ImmutableList.copyOf(trustManagers); - } - - public static TrustManager[] getTrustManagersFromKeyStores(KeyStore... keyStore) { - return new TrustManager[] { new HttpsMultiTrustManager(keyStore) }; - - } - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkClientTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - for (X509TrustManager trustManager : trustManagers) { - try { - trustManager.checkServerTrusted(chain, authType); - return; - } catch (CertificateException ignored) {} - } - throw new CertificateException("None of the TrustManagers trust this certificate chain"); - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - ImmutableList.Builder certificates = ImmutableList.builder(); - for (X509TrustManager trustManager : trustManagers) { - for (X509Certificate cert : trustManager.getAcceptedIssuers()) { - certificates.add(cert); - } - } - return Iterables.toArray(certificates.build(), X509Certificate.class); - } - - public X509TrustManager getTrustManager(KeyStore keystore) { - return getTrustManager(TrustManagerFactory.getDefaultAlgorithm(), keystore); - } - - public X509TrustManager getTrustManager(String algorithm, KeyStore keystore) { - TrustManagerFactory factory; - try { - factory = TrustManagerFactory.getInstance(algorithm); - factory.init(keystore); - return Iterables.getFirst(Iterables.filter( - Arrays.asList(factory.getTrustManagers()), X509TrustManager.class), null); - } catch (NoSuchAlgorithmException | KeyStoreException e) { - e.printStackTrace(); - } - return null; - } -} diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java index aac082a0133..36550420909 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/CopyCommand.java @@ -93,4 +93,9 @@ public class CopyCommand extends StorageSubSystemCommand { public void setExecuteInSequence(final boolean inSeq) { executeInSequence = inSeq; } + + @Override + public boolean isReconcile() { + return true; + } } diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index 76b93909b8c..0c3bb99e75c 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -35,12 +35,16 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { private VolumeObjectTO volume; private String parentSnapshotPath; private DataStoreTO dataStore; + private DataStoreTO imageStore; + private boolean kvmIncrementalSnapshot = false; private String vmName; private String name; private HypervisorType hypervisorType; private long id; private boolean quiescevm; private String[] parents; + private DataStoreTO parentStore; + private String checkpointPath; private Long physicalSize = (long) 0; private long accountId; @@ -49,6 +53,11 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { } + @Override + public DataObjectType getObjectType() { + return DataObjectType.SNAPSHOT; + } + public SnapshotObjectTO(SnapshotInfo snapshot) { this.path = snapshot.getPath(); this.setId(snapshot.getId()); @@ -59,27 +68,28 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.setVmName(vol.getAttachedVmName()); } - SnapshotInfo parentSnapshot = snapshot.getParent(); - ArrayList parentsArry = new ArrayList(); - if (parentSnapshot != null) { - this.parentSnapshotPath = parentSnapshot.getPath(); - while(parentSnapshot != null) { - parentsArry.add(parentSnapshot.getPath()); - parentSnapshot = parentSnapshot.getParent(); - } - parents = parentsArry.toArray(new String[parentsArry.size()]); - ArrayUtils.reverse(parents); - } - this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); this.hypervisorType = snapshot.getHypervisorType(); this.quiescevm = false; - } - @Override - public DataObjectType getObjectType() { - return DataObjectType.SNAPSHOT; + this.checkpointPath = snapshot.getCheckpointPath(); + this.kvmIncrementalSnapshot = snapshot.isKvmIncrementalSnapshot(); + + SnapshotInfo parentSnapshot = snapshot.getParent(); + + if (parentSnapshot == null || (HypervisorType.KVM.equals(snapshot.getHypervisorType()) && !parentSnapshot.isKvmIncrementalSnapshot())) { + return; + } + + ArrayList parentsArray = new ArrayList<>(); + this.parentSnapshotPath = parentSnapshot.getPath(); + while (parentSnapshot != null) { + parentsArray.add(parentSnapshot.getPath()); + parentSnapshot = parentSnapshot.getParent(); + } + parents = parentsArray.toArray(new String[parentsArray.size()]); + ArrayUtils.reverse(parents); } @Override @@ -91,6 +101,30 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.dataStore = store; } + public DataStoreTO getImageStore() { + return imageStore; + } + + public void setImageStore(DataStoreTO imageStore) { + this.imageStore = imageStore; + } + + public boolean isKvmIncrementalSnapshot() { + return kvmIncrementalSnapshot; + } + + public void setKvmIncrementalSnapshot(boolean kvmIncrementalSnapshot) { + this.kvmIncrementalSnapshot = kvmIncrementalSnapshot; + } + + public String getCheckpointPath() { + return checkpointPath; + } + + public void setCheckpointPath(String checkpointPath) { + this.checkpointPath = checkpointPath; + } + @Override public String getPath() { return this.path; @@ -178,6 +212,14 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO { this.accountId = accountId; } + public DataStoreTO getParentStore() { + return parentStore; + } + + public void setParentStore(DataStoreTO parentStore) { + this.parentStore = parentStore; + } + @Override public String toString() { return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path").append(path).append("]").toString(); diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java index 4d1d0bf9097..92f9ee497a4 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -33,6 +33,8 @@ import com.cloud.storage.Volume; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import java.util.Arrays; +import java.util.List; +import java.util.Set; public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { private String uuid; @@ -76,6 +78,8 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { @LogLevel(LogLevel.Log4jLevel.Off) private byte[] passphrase; private String encryptFormat; + private List checkpointPaths; + private Set checkpointImageStoreUrls; public VolumeObjectTO() { @@ -122,6 +126,8 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { this.passphrase = volume.getPassphrase(); this.encryptFormat = volume.getEncryptFormat(); this.followRedirects = volume.isFollowRedirects(); + this.checkpointPaths = volume.getCheckpointPaths(); + this.checkpointImageStoreUrls = volume.getCheckpointImageStoreUrls(); } public String getUuid() { @@ -397,4 +403,21 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO { public boolean requiresEncryption() { return passphrase != null && passphrase.length > 0; } + + + public List getCheckpointPaths() { + return checkpointPaths; + } + + public void setCheckpointPaths(List checkpointPaths) { + this.checkpointPaths = checkpointPaths; + } + + public Set getCheckpointImageStoreUrls() { + return checkpointImageStoreUrls; + } + + public void setCheckpointImageStoreUrls(Set checkpointImageStoreUrls) { + this.checkpointImageStoreUrls = checkpointImageStoreUrls; + } } diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java index 042bec9d216..6d4c6234c42 100644 --- a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/ConfigHelperTest.java @@ -57,6 +57,7 @@ import com.cloud.agent.resource.virtualnetwork.model.IpAssociation; import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRule; import com.cloud.agent.resource.virtualnetwork.model.LoadBalancerRules; import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.Networks.TrafficType; public class ConfigHelperTest { @@ -223,9 +224,12 @@ public class ConfigHelperTest { protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand() { final List lbs = new ArrayList<>(); final List dests = new ArrayList<>(); + final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false); dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); - lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); + LoadBalancerTO loadBalancerTO = new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests); + loadBalancerTO.setLbSslCert(lbSslCert); + lbs.add(loadBalancerTO); final LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; lbs.toArray(arrayLbs); diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java new file mode 100644 index 00000000000..f2b25a5f78e --- /dev/null +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/model/LoadBalancerRuleTest.java @@ -0,0 +1,63 @@ +// 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.resource.virtualnetwork.model; + +import org.junit.Assert; +import org.junit.Test; + +public class LoadBalancerRuleTest { + + @Test + public void testSslCertEntry() { + String name = "name"; + String cert = "cert"; + String key = "key1"; + String chain = "chain"; + String password = "password"; + LoadBalancerRule.SslCertEntry sslCertEntry = new LoadBalancerRule.SslCertEntry(name, cert, key, chain, password); + + Assert.assertEquals(name, sslCertEntry.getName()); + Assert.assertEquals(cert, sslCertEntry.getCert()); + Assert.assertEquals(key, sslCertEntry.getKey()); + Assert.assertEquals(chain, sslCertEntry.getChain()); + Assert.assertEquals(password, sslCertEntry.getPassword()); + + String name2 = "name2"; + String cert2 = "cert2"; + String key2 = "key2"; + String chain2 = "chain2"; + String password2 = "password2"; + + sslCertEntry.setName(name2); + sslCertEntry.setCert(cert2); + sslCertEntry.setKey(key2); + sslCertEntry.setChain(chain2); + sslCertEntry.setPassword(password2); + + Assert.assertEquals(name2, sslCertEntry.getName()); + Assert.assertEquals(cert2, sslCertEntry.getCert()); + Assert.assertEquals(key2, sslCertEntry.getKey()); + Assert.assertEquals(chain2, sslCertEntry.getChain()); + Assert.assertEquals(password2, sslCertEntry.getPassword()); + + LoadBalancerRule loadBalancerRule = new LoadBalancerRule(); + loadBalancerRule.setSslCerts(new LoadBalancerRule.SslCertEntry[]{sslCertEntry}); + + Assert.assertEquals(1, loadBalancerRule.getSslCerts().length); + Assert.assertEquals(sslCertEntry, loadBalancerRule.getSslCerts()[0]); + } +} diff --git a/core/src/test/java/com/cloud/agent/transport/ResponseTest.java b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java new file mode 100644 index 00000000000..06869b42eb9 --- /dev/null +++ b/core/src/test/java/com/cloud/agent/transport/ResponseTest.java @@ -0,0 +1,46 @@ +// +// 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.transport; + +import junit.framework.TestCase; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.junit.Assert; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer; + +import com.cloud.agent.transport.Request.Version; + +public class ResponseTest extends TestCase { + protected Logger logger = LogManager.getLogger(getClass()); + + public void testCheckS2SVpnConnectionsAnswer() { + logger.info("Testing CheckS2SVpnConnectionsAnswer"); + String content = "[{\"com.cloud.agent.api.CheckS2SVpnConnectionsAnswer\":{\"ipToConnected\":{\"10.0.53.13\":true}," + + "\"ipToDetail\":{\"10.0.53.13\":\"IPsec SA found;Site-to-site VPN have connected\"}," + + "\"details\":\"10.0.53.13:0:IPsec SA found;Site-to-site VPN have connected\\u0026\\n\"," + + "\"result\":true,\"contextMap\":{},\"wait\":0,\"bypassHostMaintenance\":false}}]"; + Response response = new Response(Version.v2, 1L, 2L, 3L, 1L, (short)1, content); + Answer answer = response.getAnswer(); + Assert.assertTrue(answer instanceof CheckS2SVpnConnectionsAnswer); + } + +} diff --git a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java index 2a282cbeca8..72361c2880e 100644 --- a/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java +++ b/core/src/test/java/com/cloud/network/HAProxyConfiguratorTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.network.lb.LoadBalancingRule.LbDestination; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import java.util.List; import java.util.ArrayList; @@ -80,11 +81,11 @@ public class HAProxyConfiguratorTest { HAProxyConfigurator hpg = new HAProxyConfigurator(); LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false); String result = genConfig(hpg, cmd); - assertTrue("keepalive disabled should result in 'mode http' in the resulting haproxy config", result.contains("mode http")); + assertTrue("keepalive disabled should result in 'option httpclose' in the resulting haproxy config", result.contains("\toption httpclose")); cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "4", true); result = genConfig(hpg, cmd); - assertTrue("keepalive enabled should not result in 'mode http' in the resulting haproxy config", !result.contains("mode http")); + assertTrue("keepalive enabled should result in 'no option httpclose' in the resulting haproxy config", result.contains("\tno option httpclose")); // TODO // create lb command // setup tests for @@ -122,6 +123,19 @@ public class HAProxyConfiguratorTest { Assert.assertTrue(result.contains("acl network_allowed src 1.1.1.1 2.2.2.2/24 \n\ttcp-request connection reject if !network_allowed")); } + @Test + public void generateConfigurationTestWithSslCert() { + LoadBalancerTO lb = new LoadBalancerTO("1", "10.2.0.1", 443, "ssl", "roundrobin", false, false, false, null); + final LbSslCert lbSslCert = new LbSslCert("cert", "key", "password", "chain", "fingerprint", false); + lb.setLbSslCert(lbSslCert); + LoadBalancerTO[] lba = new LoadBalancerTO[1]; + lba[0] = lb; + HAProxyConfigurator hpg = new HAProxyConfigurator(); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(lba, "10.0.0.1", "10.1.0.1", "10.1.1.1", null, 1L, "12", false); + String result = genConfig(hpg, cmd); + Assert.assertTrue(result.contains("bind 10.2.0.1:443 ssl crt /etc/cloudstack/ssl/10_2_0_1-443.pem")); + } + private String genConfig(HAProxyConfigurator hpg, LoadBalancerConfigCommand cmd) { String[] sa = hpg.generateConfiguration(cmd); StringBuilder sb = new StringBuilder(); diff --git a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java index 3f68422d502..f021009cec0 100644 --- a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java +++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckGuestOsMappingCommandTest.java @@ -25,8 +25,6 @@ import static org.junit.Assert.assertFalse; import com.cloud.agent.api.CheckGuestOsMappingCommand; import org.junit.Test; -import com.cloud.agent.api.AgentControlCommand; - public class CheckGuestOsMappingCommandTest { @Test diff --git a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java index be7563be045..a696049608e 100644 --- a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java +++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java @@ -284,6 +284,11 @@ public class CheckOnHostCommandTest { public CPU.CPUArch getArch() { return CPU.CPUArch.amd64; } + + @Override + public String getStorageAccessGroups() { + return null; + } }; CheckOnHostCommand cohc = new CheckOnHostCommand(host); diff --git a/core/src/test/java/org/apache/cloudstack/command/ReconcileCommandUtilsTest.java b/core/src/test/java/org/apache/cloudstack/command/ReconcileCommandUtilsTest.java new file mode 100644 index 00000000000..f69ada58e1a --- /dev/null +++ b/core/src/test/java/org/apache/cloudstack/command/ReconcileCommandUtilsTest.java @@ -0,0 +1,69 @@ +// 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.command; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MigrateCommand; +import com.cloud.agent.api.to.VirtualMachineTO; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.File; + +public class ReconcileCommandUtilsTest { + + final static String COMMANDS_LOG_PATH = "/tmp"; + + @Test + public void createAndReadAndDeleteLogFilesForCommand() { + final String vmName = "TestVM"; + final String destIp = "DestinationHost"; + final VirtualMachineTO vmTO = Mockito.mock(VirtualMachineTO.class); + final MigrateCommand command = new MigrateCommand(vmName, destIp, false, vmTO, false); + long requestSequence = 10000L; + command.setRequestSequence(requestSequence); + + String logFile = ReconcileCommandUtils.getLogFileNameForCommand(COMMANDS_LOG_PATH, command); + + ReconcileCommandUtils.createLogFileForCommand(COMMANDS_LOG_PATH, command); + Assert.assertTrue((new File(logFile).exists())); + + CommandInfo commandInfo = ReconcileCommandUtils.readLogFileForCommand(logFile); + Assert.assertNotNull(commandInfo); + Assert.assertEquals(command.getClass().getName(), commandInfo.getCommandName()); + Assert.assertEquals(Command.State.CREATED, commandInfo.getState()); + + Command parseCommand = ReconcileCommandUtils.parseCommandInfo(commandInfo); + System.out.println("command state is " + commandInfo); + Assert.assertNotNull(parseCommand); + Assert.assertTrue(parseCommand instanceof MigrateCommand); + Assert.assertEquals(vmName,((MigrateCommand) parseCommand).getVmName()); + Assert.assertEquals(destIp,((MigrateCommand) parseCommand).getDestinationIp()); + + ReconcileCommandUtils.updateLogFileForCommand(COMMANDS_LOG_PATH, command, Command.State.PROCESSING); + CommandInfo newCommandInfo = ReconcileCommandUtils.readLogFileForCommand(logFile); + System.out.println("new command state is " + newCommandInfo); + Assert.assertNotNull(newCommandInfo); + Assert.assertEquals(command.getClass().getName(), newCommandInfo.getCommandName()); + Assert.assertEquals(Command.State.PROCESSING, newCommandInfo.getState()); + + ReconcileCommandUtils.deleteLogFileForCommand(COMMANDS_LOG_PATH, command); + Assert.assertFalse((new File(logFile).exists())); + } +} diff --git a/debian/changelog b/debian/changelog index d17676ef268..ace6697a09e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +cloudstack (4.22.0.0-SNAPSHOT) unstable; urgency=low + + * Update the version to 4.22.0.0-SNAPSHOT + + -- the Apache CloudStack project Thu, Aug 28 11:58:36 2025 +0530 + +cloudstack (4.21.0.0) unstable; urgency=low + + * Update the version to 4.21.0.0 + + -- the Apache CloudStack project Fri, 22 Aug 2025 11:42:37 +0530 + cloudstack (4.21.0.0-SNAPSHOT) unstable; urgency=low * Update the version to 4.21.0.0-SNAPSHOT @@ -196,7 +208,7 @@ cloud (4.0.0-rc2) unstable; urgency=low * Bumping the version to 4.0 RC2 -- Wido den Hollander Mon, 24 Sep 2012 11:30:29 +0200 - + cloud (4.0.0-rc1) unstable; urgency=low * Bumping the version to 4.0 RC1 diff --git a/debian/cloudstack-common.install b/debian/cloudstack-common.install index 08f56d4f117..51fc5bf2a66 100644 --- a/debian/cloudstack-common.install +++ b/debian/cloudstack-common.install @@ -27,6 +27,7 @@ /usr/share/cloudstack-common/scripts/vm/hypervisor/versions.sh /usr/share/cloudstack-common/scripts/vm/hypervisor/vmware/* /usr/share/cloudstack-common/scripts/vm/hypervisor/xenserver/* +/usr/share/cloudstack-common/scripts/vm/hypervisor/external/provisioner/* /usr/share/cloudstack-common/lib/* /usr/share/cloudstack-common/vms/* /usr/bin/cloudstack-set-guest-password diff --git a/debian/cloudstack-management.install b/debian/cloudstack-management.install index 3d0d7e23814..b2a32bd93c1 100644 --- a/debian/cloudstack-management.install +++ b/debian/cloudstack-management.install @@ -22,6 +22,8 @@ /etc/cloudstack/management/java.security.ciphers /etc/cloudstack/management/log4j-cloud.xml /etc/cloudstack/management/config.json +/etc/cloudstack/extensions/Proxmox/proxmox.sh +/etc/cloudstack/extensions/HyperV/hyperv.py /etc/default/cloudstack-management /etc/security/limits.d/cloudstack-limits.conf /etc/sudoers.d/cloudstack diff --git a/debian/cloudstack-management.postinst b/debian/cloudstack-management.postinst index fadb7e57eca..fde3ba96de0 100755 --- a/debian/cloudstack-management.postinst +++ b/debian/cloudstack-management.postinst @@ -56,6 +56,11 @@ if [ "$1" = configure ]; then chmod 0640 ${CONFDIR}/${DBPROPS} chgrp cloud ${CONFDIR}/${DBPROPS} chown -R cloud:cloud /var/log/cloudstack/management + chown -R cloud:cloud /usr/share/cloudstack-management/templates + find /usr/share/cloudstack-management/templates -type d -exec chmod 0770 {} \; + + chmod -R 0755 /etc/cloudstack/extensions + chown -R cloud:cloud /etc/cloudstack/extensions ln -sf ${CONFDIR}/log4j-cloud.xml ${CONFDIR}/log4j2.xml diff --git a/debian/rules b/debian/rules index e7ff6759d44..4c3023aeae2 100755 --- a/debian/rules +++ b/debian/rules @@ -64,12 +64,14 @@ override_dh_auto_install: # cloudstack-management mkdir $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/server mkdir $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management + mkdir -p $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/extensions mkdir -p $(DESTDIR)/$(SYSCONFDIR)/security/limits.d/ mkdir -p $(DESTDIR)/$(SYSCONFDIR)/sudoers.d/ mkdir -p $(DESTDIR)/usr/share/$(PACKAGE)-management mkdir -p $(DESTDIR)/usr/share/$(PACKAGE)-management/lib mkdir -p $(DESTDIR)/usr/share/$(PACKAGE)-management/setup mkdir -p $(DESTDIR)/usr/share/$(PACKAGE)-management/templates/systemvm + mkdir -p $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf mkdir $(DESTDIR)/var/log/$(PACKAGE)/management mkdir $(DESTDIR)/var/cache/$(PACKAGE)/management mkdir $(DESTDIR)/var/log/$(PACKAGE)/ipallocator @@ -80,10 +82,12 @@ override_dh_auto_install: cp -r client/target/classes/META-INF/webapp $(DESTDIR)/usr/share/$(PACKAGE)-management/webapp cp server/target/conf/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/server/ cp client/target/conf/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/management/ + cp -r extensions/* $(DESTDIR)/$(SYSCONFDIR)/$(PACKAGE)/extensions/ cp client/target/cloud-client-ui-$(VERSION).jar $(DESTDIR)/usr/share/$(PACKAGE)-management/lib/cloudstack-$(VERSION).jar cp client/target/lib/*jar $(DESTDIR)/usr/share/$(PACKAGE)-management/lib/ cp -r engine/schema/dist/systemvm-templates/* $(DESTDIR)/usr/share/$(PACKAGE)-management/templates/systemvm/ - rm -rf $(DESTDIR)/usr/share/$(PACKAGE)-management/templates/systemvm/md5sum.txt + cp -r plugins/integrations/kubernetes-service/src/main/resources/conf/* $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf/ + rm -rf $(DESTDIR)/usr/share/$(PACKAGE)-management/templates/systemvm/sha512sum.txt # Bundle cmk in cloudstack-management wget https://github.com/apache/cloudstack-cloudmonkey/releases/download/$(CMK_REL)/cmk.linux.x86-64 -O $(DESTDIR)/usr/bin/cmk @@ -95,9 +99,16 @@ override_dh_auto_install: chmod 0440 $(DESTDIR)/$(SYSCONFDIR)/sudoers.d/$(PACKAGE) install -D client/target/utilities/bin/cloud-update-xenserver-licenses $(DESTDIR)/usr/bin/cloudstack-update-xenserver-licenses + + install -D plugins/integrations/kubernetes-service/src/main/resources/conf/etcd-node.yml $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf/etcd-node.yml + install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf/k8s-control-node.yml + install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node-add.yml $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf/k8s-control-node-add.yml + install -D plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-node.yml $(DESTDIR)/usr/share/$(PACKAGE)-management/cks/conf/k8s-node.yml + # Remove configuration in /ur/share/cloudstack-management/webapps/client/WEB-INF # This should all be in /etc/cloudstack/management ln -s ../../..$(SYSCONFDIR)/$(PACKAGE)/management $(DESTDIR)/usr/share/$(PACKAGE)-management/conf + ln -s ../../..$(SYSCONFDIR)/$(PACKAGE)/extensions $(DESTDIR)/usr/share/$(PACKAGE)-management/extensions ln -s ../../../var/log/$(PACKAGE)/management $(DESTDIR)/usr/share/$(PACKAGE)-management/logs install -d -m0755 debian/$(PACKAGE)-management/lib/systemd/system diff --git a/deps/install-non-oss.sh b/deps/install-non-oss.sh index 56cfc2cba9c..e40946ce312 100755 --- a/deps/install-non-oss.sh +++ b/deps/install-non-oss.sh @@ -85,4 +85,7 @@ mvn install:install-file -Dfile=juniper-contrail-api-1.0-SNAPSHOT.jar -DgroupId= # From https://github.com/radu-todirica/tungsten-api/raw/master/net/juniper/tungsten/juniper-tungsten-api/2.0/juniper-tungsten-api-2.0.jar mvn install:install-file -Dfile=juniper-tungsten-api-2.0.jar -DgroupId=net.juniper.tungsten -DartifactId=juniper-tungsten-api -Dversion=2.0 -Dpackaging=jar +# Netris Integration +mvn install:install-file -Dfile=netris-java-sdk-1.0.0.jar -DgroupId=io.netris -DartifactId=netris-java-sdk -Dversion=1.0.0 -Dpackaging=jar + exit 0 diff --git a/developer/pom.xml b/developer/pom.xml index c573c03d008..c11415e04fa 100644 --- a/developer/pom.xml +++ b/developer/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT diff --git a/engine/api/pom.xml b/engine/api/pom.xml index 25aaa1365df..9dcdb8e8f52 100644 --- a/engine/api/pom.xml +++ b/engine/api/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT ../pom.xml diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java index f8032bf4b0e..76f0830f369 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineGuru.java @@ -24,6 +24,7 @@ import com.cloud.utils.PasswordGenerator; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.framework.ca.Certificate; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.utils.security.CertUtils; import org.apache.cloudstack.utils.security.KeyStoreUtils; @@ -37,6 +38,9 @@ import java.util.Base64; */ public interface VirtualMachineGuru { + static final ConfigKey NTPServerConfig = new ConfigKey(String.class, "ntp.server.list", "Advanced", null, + "Comma separated list of NTP servers to configure in System VMs", true, ConfigKey.Scope.Global, null, null, null, null, null, ConfigKey.Kind.CSV, null); + boolean finalizeVirtualMachineProfile(VirtualMachineProfile profile, DeployDestination dest, ReservationContext context); /** diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 94c73d8f4d6..c05c29add55 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.vm; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; import java.net.URI; import java.util.HashMap; import java.util.LinkedHashMap; @@ -122,6 +124,7 @@ public interface VirtualMachineManager extends Manager { * @param defaultNetwork The default network for the VM. * @param rootDiskOffering For created VMs not based on templates, root disk offering specifies the root disk. * @param dataDiskOfferings Data disks to attach to the VM. + * @param dataDiskDeviceIds Device Ids to assign the data disks to. * @param auxiliaryNetworks additional networks to attach the VMs to. * @param plan How to deploy the VM. * @param hyperType Hypervisor type @@ -129,11 +132,11 @@ public interface VirtualMachineManager extends Manager { * @throws InsufficientCapacityException If there are insufficient capacity to deploy this vm. */ void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, DiskOfferingInfo rootDiskOfferingInfo, - List dataDiskOfferings, LinkedHashMap> auxiliaryNetworks, DeploymentPlan plan, - HypervisorType hyperType, Map> extraDhcpOptions, Map datadiskTemplateToDiskOfferingMap) throws InsufficientCapacityException; + List dataDiskOfferings, List dataDiskDeviceIds, LinkedHashMap> auxiliaryNetworks, DeploymentPlan plan, + HypervisorType hyperType, Map> extraDhcpOptions, Map datadiskTemplateToDiskOfferingMap, Volume volume, Snapshot snapshot) throws InsufficientCapacityException; void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, - LinkedHashMap> networkProfiles, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException; + LinkedHashMap> networkProfiles, DeploymentPlan plan, HypervisorType hyperType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException; void start(String vmUuid, Map params); @@ -309,4 +312,8 @@ public interface VirtualMachineManager extends Manager { Map getDiskOfferingSuitabilityForVm(long vmId, List diskOfferingIds); + void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTemplate template, + ServiceOffering serviceOffering, Account systemAccount, DeploymentPlan plan) + throws InsufficientServerCapacityException; + } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index 8463d9cee98..adce5f2f8b4 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -112,6 +112,9 @@ public interface NetworkOrchestrationService { static final ConfigKey NSX_ENABLED = new ConfigKey<>(Boolean.class, "nsx.plugin.enable", "Advanced", "false", "Indicates whether to enable the NSX plugin", false, ConfigKey.Scope.Zone, null); + ConfigKey NETRIS_ENABLED = new ConfigKey<>(Boolean.class, "netris.plugin.enable", "Advanced", "false", + "Indicates whether to enable the Netris plugin", false, ConfigKey.Scope.Zone, null); + List setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault) throws ConcurrentOperationException; diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index 7950dda4d68..ccb5bba1c0a 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Set; import com.cloud.exception.ResourceAllocationException; +import com.cloud.utils.Pair; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; @@ -83,6 +84,17 @@ public interface VolumeOrchestrationService { "The maximum size for a volume (in GiB).", true); + ConfigKey VolumeAllocationAlgorithm = new ConfigKey<>( + String.class, + "volume.allocation.algorithm", + "Advanced", + "random", + "Order in which storage pool within a cluster will be considered for volume allocation. The value can be 'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', or 'firstfitleastconsumed'.", + true, + ConfigKey.Scope.Global, null, null, null, null, null, + ConfigKey.Kind.Select, + "random,firstfit,userdispersing,userconcentratedpod_random,userconcentratedpod_firstfit,firstfitleastconsumed"); + VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) throws ConcurrentOperationException, StorageUnavailableException; @@ -137,7 +149,7 @@ public interface VolumeOrchestrationService { * Allocate a volume or multiple volumes in case of template is registered with the 'deploy-as-is' option, allowing multiple disks */ List allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, - Account owner); + Account owner, Volume volume, Snapshot snapshot); String getVmNameFromVolumeId(long volumeId); @@ -179,4 +191,9 @@ public interface VolumeOrchestrationService { * Unmanage VM volumes */ void unmanageVolumes(long vmId); + + /** + * Retrieves the volume's checkpoints paths to be used in the KVM processor. If there are no checkpoints, it will return an empty list. + */ + Pair, Set> getVolumeCheckpointPathsAndImageStoreUrls(long volumeId, HypervisorType hypervisorType); } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java index 3ffa496b544..6be71b3cb25 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.engine.service.api; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; import java.net.URL; import java.util.List; import java.util.Map; @@ -38,6 +40,7 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering; import com.cloud.vm.NicProfile; +import com.cloud.vm.VmDiskInfo; @Path("orchestration") @Produces({"application/json", "application/xml"}) @@ -62,20 +65,22 @@ public interface OrchestrationService { @POST @Path("/createvm") VirtualMachineEntity createVirtualMachine(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("template-id") String templateId, - @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor, - @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize, - @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, - @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, - @QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, - @QueryParam("datadisktemplate-diskoffering-map") Map datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId, @QueryParam("root-disk-offering-id") Long rootDiskOfferingId) throws InsufficientCapacityException; + @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor, + @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize, + @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, + @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, + @QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, + @QueryParam("datadisktemplate-diskoffering-map") Map datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId, + @QueryParam("root-disk-offering-id") Long rootDiskOfferingId, List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException; @POST VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId, - @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor, - @QueryParam("os") String os, @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize, - @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, - @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, - @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId) throws InsufficientCapacityException; + @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor, + @QueryParam("os") String os, @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize, + @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, + @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, + @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId, + @QueryParam("data-disks-offering-info") List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException; @POST NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java index f537d8f5202..2494cc7c5fc 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreCapabilities.java @@ -40,5 +40,13 @@ public enum DataStoreCapabilities { /** * indicates that this driver supports reverting a volume to a snapshot state */ - CAN_REVERT_VOLUME_TO_SNAPSHOT + CAN_REVERT_VOLUME_TO_SNAPSHOT, + /** + * indicates that the driver supports copying snapshot between zones on pools of the same type + */ + CAN_COPY_SNAPSHOT_BETWEEN_ZONES_AND_SAME_POOL_TYPE, + /** + * indicates that this driver supports the option to create a template from the back-end snapshot + */ + CAN_CREATE_TEMPLATE_FROM_SNAPSHOT } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java index b197afad863..af3b38b368c 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java @@ -45,4 +45,8 @@ public interface DataStoreDriver { boolean canCopy(DataObject srcData, DataObject destData); void resize(DataObject data, AsyncCompletionCallback callback); + + default boolean canDisplayDetails() { + return true; + } } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java index 4f2a69bc771..7cf2a593db4 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import com.cloud.hypervisor.Hypervisor; + import java.util.List; public interface EndPointSelector { @@ -39,6 +41,8 @@ public interface EndPointSelector { EndPoint select(DataObject object, StorageAction action, boolean encryptionSupportRequired); + EndPoint selectRandom(long zoneId, Hypervisor.HypervisorType hypervisorType); + List selectAll(DataStore store); List findAllEndpointsForScope(DataStore store); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java index 611d1247c49..ce9f4a67a73 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java @@ -32,7 +32,8 @@ public interface ObjectInDataStoreStateMachine extends StateObject + + org.apache.cloudstack + cloud-plugin-hypervisor-external + ${project.version} + diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java index 30a58d405c9..0a6bbc87654 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java @@ -32,7 +32,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import com.cloud.agent.api.CleanupPersistentNetworkResourceCommand; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.agent.lb.SetupMSListCommand; +import org.apache.cloudstack.command.ReconcileAnswer; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.logging.log4j.Logger; @@ -114,6 +118,7 @@ public abstract class AgentAttache { protected final long _id; protected String _uuid; protected String _name = null; + protected HypervisorType _hypervisorType; protected final ConcurrentHashMap _waitForList; protected final LinkedList _requests; protected Long _currentSequence; @@ -135,10 +140,11 @@ public abstract class AgentAttache { Arrays.sort(s_commandsNotAllowedInConnectingMode); } - protected AgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final boolean maintenance) { + protected AgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final HypervisorType hypervisorType, final boolean maintenance) { _id = id; _uuid = uuid; _name = name; + _hypervisorType = hypervisorType; _waitForList = new ConcurrentHashMap(); _currentSequence = null; _maintenance = maintenance; @@ -261,6 +267,10 @@ public abstract class AgentAttache { return _name; } + public HypervisorType get_hypervisorType() { + return _hypervisorType; + } + public int getQueueSize() { return _requests.size(); } @@ -406,10 +416,17 @@ public abstract class AgentAttache { try { for (int i = 0; i < 2; i++) { Answer[] answers = null; - try { - answers = sl.waitFor(wait); - } catch (final InterruptedException e) { - logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Interrupted"); + Command[] cmds = req.getCommands(); + if (cmds != null && cmds.length == 1 && (cmds[0] != null) && cmds[0].isReconcile() + && !sl.isDisconnected() && _agentMgr.isReconcileCommandsEnabled(_hypervisorType)) { + // only available if (1) the only command is a Reconcile command (2) agent is connected; (3) reconciliation is enabled; (4) hypervisor is KVM; + answers = waitForAnswerOfReconcileCommand(sl, seq, cmds[0], wait); + } else { + try { + answers = sl.waitFor(wait); + } catch (final InterruptedException e) { + logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Interrupted"); + } } if (answers != null) { new Response(req, answers).logD("Received: ", false); @@ -428,11 +445,13 @@ public abstract class AgentAttache { if (current != null && seq != current) { logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Waited too long."); + _agentMgr.updateReconcileCommandsIfNeeded(req.getSequence(), req.getCommands(), Command.State.TIMED_OUT); throw new OperationTimedoutException(req.getCommands(), _id, seq, wait, false); } logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Waiting some more time because this is the current command"); } + _agentMgr.updateReconcileCommandsIfNeeded(req.getSequence(), req.getCommands(), Command.State.TIMED_OUT); throw new OperationTimedoutException(req.getCommands(), _id, seq, wait * 2, true); } catch (OperationTimedoutException e) { logger.warn(LOG_SEQ_FORMATTED_STRING, seq, "Timed out on " + req.toString()); @@ -449,12 +468,57 @@ public abstract class AgentAttache { if (req.executeInSequence() && (current != null && current == seq)) { sendNext(seq); } + _agentMgr.updateReconcileCommandsIfNeeded(req.getSequence(), req.getCommands(), Command.State.TIMED_OUT); throw new OperationTimedoutException(req.getCommands(), _id, seq, wait, false); } finally { unregisterListener(seq); } } + private Answer[] waitForAnswerOfReconcileCommand(SynchronousListener sl, final long seq, final Command command, final int wait) { + Answer[] answers = null; + int waitTimeLeft = wait; + while (waitTimeLeft > 0) { + int waitTime = Math.min(waitTimeLeft, _agentMgr.getReconcileInterval()); + logger.debug(String.format("Waiting %s seconds for the answer of reconcile command %s-%s", waitTime, seq, command)); + if (sl.isDisconnected()) { + logger.debug(String.format("Disconnected while waiting for the answer of reconcile command %s-%s", seq, command)); + break; + } + try { + answers = sl.waitFor(waitTime); + } catch (final InterruptedException e) { + logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Interrupted"); + } + if (answers != null) { + break; + } + + logger.debug(String.format("Getting the answer of reconcile command from cloudstack database for %s-%s", seq, command)); + Pair commandInfo = _agentMgr.getStateAndAnswerOfReconcileCommand(seq, command); + if (commandInfo == null) { + logger.debug(String.format("Cannot get the answer of reconcile command from cloudstack database for %s-%s", seq, command)); + continue; + } + Command.State state = commandInfo.first(); + if (Command.State.INTERRUPTED.equals(state)) { + logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Interrupted by agent, will reconcile it"); + throw new CloudRuntimeException("Interrupted by agent"); + } else if (Command.State.DANGLED_IN_BACKEND.equals(state)) { + logger.debug(LOG_SEQ_FORMATTED_STRING, seq, "Dangling in backend, it seems the agent was restarted, will reconcile it"); + throw new CloudRuntimeException("It is not being processed by agent"); + } + Answer answer = commandInfo.second(); + logger.debug(String.format("Got the answer of reconcile command from cloudstack database for %s-%s: %s", seq, command, answer)); + if (answer != null && !(answer instanceof ReconcileAnswer)) { + answers = new Answer[] { answer }; + break; + } + waitTimeLeft -= waitTime; + } + return answers; + } + protected synchronized void sendNext(final long seq) { _currentSequence = null; if (_requests.isEmpty()) { diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java index 765602e42d0..3d398ca5dd9 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java @@ -27,6 +27,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -43,6 +44,10 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.agent.lb.IndirectAgentLB; import org.apache.cloudstack.ca.CAManager; +import org.apache.cloudstack.command.ReconcileCommandService; +import org.apache.cloudstack.command.ReconcileCommandUtils; +import org.apache.cloudstack.command.ReconcileCommandVO; +import org.apache.cloudstack.command.dao.ReconcileCommandDao; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -55,9 +60,10 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao; import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.commons.collections.MapUtils; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.ThreadContext; @@ -177,6 +183,10 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected HighAvailabilityManager _haMgr = null; @Inject protected AlertManager _alertMgr = null; + @Inject + protected ReconcileCommandService reconcileCommandService; + @Inject + ReconcileCommandDao reconcileCommandDao; @Inject protected HypervisorGuruManager _hvGuruMgr; @@ -207,6 +217,9 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl private final ConcurrentHashMap newAgentConnections = new ConcurrentHashMap<>(); protected ScheduledExecutorService newAgentConnectionsMonitor; + private boolean _reconcileCommandsEnabled = false; + private Integer _reconcileCommandInterval; + @Inject ResourceManager _resourceMgr; @Inject @@ -214,15 +227,17 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected final ConfigKey Workers = new ConfigKey<>("Advanced", Integer.class, "workers", "5", "Number of worker threads handling remote agent connections.", false); - protected final ConfigKey Port = new ConfigKey<>("Advanced", Integer.class, "port", "8250", "Port to listen on for remote agent connections.", false); + protected final ConfigKey Port = new ConfigKey<>("Advanced", Integer.class, "port", "8250", "Port to listen on for remote (indirect) agent connections.", false); protected final ConfigKey RemoteAgentSslHandshakeTimeout = new ConfigKey<>("Advanced", Integer.class, "agent.ssl.handshake.timeout", "30", - "Seconds after which SSL handshake times out during remote agent connections.", false); + "Seconds after which SSL handshake times out during remote (indirect) agent connections.", false); protected final ConfigKey RemoteAgentMaxConcurrentNewConnections = new ConfigKey<>("Advanced", Integer.class, "agent.max.concurrent.new.connections", "0", - "Number of maximum concurrent new connections server allows for remote agents. " + + "Number of maximum concurrent new connections server allows for remote (indirect) agents. " + "If set to zero (default value) then no limit will be enforced on concurrent new connections", false); + protected final ConfigKey RemoteAgentNewConnectionsMonitorInterval = new ConfigKey<>("Advanced", Integer.class, "agent.connections.monitor.interval", "1800", + "Time in seconds to monitor the new agent connections and cleanup the expired connections.", false); protected final ConfigKey AlertWait = new ConfigKey<>("Advanced", Integer.class, "alert.wait", "1800", "Seconds to wait before alerting on a disconnected agent", true); protected final ConfigKey DirectAgentLoadSize = new ConfigKey<>("Advanced", Integer.class, "direct.agent.load.size", "16", @@ -234,6 +249,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected final ConfigKey CheckTxnBeforeSending = new ConfigKey<>("Developer", Boolean.class, "check.txn.before.sending.agent.commands", "false", "This parameter allows developers to enable a check to see if a transaction wraps commands that are sent to the resource. This is not to be enabled on production systems.", true); + public static final List HOST_DOWN_ALERT_UNSUPPORTED_HOST_TYPES = Arrays.asList( + Host.Type.SecondaryStorage, + Host.Type.ConsoleProxy + ); + @Override public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -255,10 +275,6 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl _executor = new ThreadPoolExecutor(agentTaskThreads, agentTaskThreads, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("AgentTaskPool")); - _connectExecutor = new ThreadPoolExecutor(100, 500, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("AgentConnectTaskPool")); - // allow core threads to time out even when there are no items in the queue - _connectExecutor.allowCoreThreadTimeOut(true); - maxConcurrentNewAgentConnections = RemoteAgentMaxConcurrentNewConnections.value(); _connection = new NioServer("AgentManager", Port.value(), Workers.value() + 10, @@ -273,12 +289,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl logger.debug("Created DirectAgentAttache pool with size: {}.", directAgentPoolSize); _directAgentThreadCap = Math.round(directAgentPoolSize * DirectAgentThreadCap.value()) + 1; // add 1 to always make the value > 0 - _monitorExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AgentMonitor")); - - newAgentConnectionsMonitor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("NewAgentConnectionsMonitor")); - initializeCommandTimeouts(); + _reconcileCommandsEnabled = ReconcileCommandService.ReconcileCommandsEnabled.value(); + _reconcileCommandInterval = ReconcileCommandService.ReconcileCommandsInterval.value(); + return true; } @@ -351,10 +366,27 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl _hostMonitors.remove(id); } + @Override + public void onManagementServerPreparingForMaintenance() { + logger.debug("Management server preparing for maintenance"); + if (_connection != null) { + _connection.block(); + } + } + + @Override + public void onManagementServerCancelPreparingForMaintenance() { + logger.debug("Management server cancel preparing for maintenance"); + if (_connection != null) { + _connection.unblock(); + } + } + @Override public void onManagementServerMaintenance() { logger.debug("Management server maintenance enabled"); _monitorExecutor.shutdownNow(); + newAgentConnectionsMonitor.shutdownNow(); if (_connection != null) { _connection.stop(); @@ -371,10 +403,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl public void onManagementServerCancelMaintenance() { logger.debug("Management server maintenance disabled"); if (_connectExecutor.isShutdown()) { - _connectExecutor = new ThreadPoolExecutor(100, 500, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("AgentConnectTaskPool")); - _connectExecutor.allowCoreThreadTimeOut(true); + initConnectExecutor(); } - startDirectlyConnectedHosts(true); if (_connection != null) { try { @@ -385,9 +415,28 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } if (_monitorExecutor.isShutdown()) { - _monitorExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AgentMonitor")); - _monitorExecutor.scheduleWithFixedDelay(new MonitorTask(), mgmtServiceConf.getPingInterval(), mgmtServiceConf.getPingInterval(), TimeUnit.SECONDS); + initAndScheduleMonitorExecutor(); } + if (newAgentConnectionsMonitor.isShutdown()) { + initAndScheduleAgentConnectionsMonitor(); + } + } + + private void initConnectExecutor() { + _connectExecutor = new ThreadPoolExecutor(100, 500, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new NamedThreadFactory("AgentConnectTaskPool")); + // allow core threads to time out even when there are no items in the queue + _connectExecutor.allowCoreThreadTimeOut(true); + } + + private void initAndScheduleMonitorExecutor() { + _monitorExecutor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AgentMonitor")); + _monitorExecutor.scheduleWithFixedDelay(new MonitorTask(), mgmtServiceConf.getPingInterval(), mgmtServiceConf.getPingInterval(), TimeUnit.SECONDS); + } + + private void initAndScheduleAgentConnectionsMonitor() { + final int cleanupTimeInSecs = Wait.value(); + newAgentConnectionsMonitor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("NewAgentConnectionsMonitor")); + newAgentConnectionsMonitor.scheduleAtFixedRate(new AgentNewConnectionsMonitorTask(), cleanupTimeInSecs, cleanupTimeInSecs, TimeUnit.SECONDS); } private AgentControlAnswer handleControlCommand(final AgentAttache attache, final AgentControlCommand cmd) { @@ -426,16 +475,6 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl return attache; } - @Override - public List getLastAgents() { - return lastAgents; - } - - @Override - public void setLastAgents(List lastAgents) { - this.lastAgents = lastAgents; - } - @Override public Answer sendTo(final Long dcId, final HypervisorType type, final Command cmd) { final List clusters = _clusterDao.listByDcHyType(dcId, type.toString()); @@ -623,7 +662,13 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl final Request req = new Request(hostId, agent.getName(), _nodeId, cmds, commands.stopOnError(), true); req.setSequence(agent.getNextSequence()); + + reconcileCommandService.persistReconcileCommands(hostId, req.getSequence(), cmds); + final Answer[] answers = agent.send(req, wait); + + reconcileCommandService.processAnswers(req.getSequence(), cmds, answers); + notifyAnswersToMonitors(hostId, req.getSequence(), answers); commands.setAnswers(answers); return answers; @@ -632,17 +677,17 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected Status investigate(final AgentAttache agent) { final Long hostId = agent.getId(); final HostVO host = _hostDao.findById(hostId); - if (host != null && host.getType() != null && !host.getType().isVirtual()) { - logger.debug("Checking if agent ({}) is alive", host); - final Answer answer = easySend(hostId, new CheckHealthCommand()); - if (answer != null && answer.getResult()) { - final Status status = Status.Up; - logger.debug("Agent ({}) responded to checkHealthCommand, reporting that agent is {}", host, status); - return status; - } - return _haMgr.investigate(hostId); + if (host == null || host.getType() == null || host.getType().isVirtual()) { + return Status.Alert; } - return Status.Alert; + logger.debug("Checking if agent ({}) is alive", host); + final Answer answer = easySend(hostId, new CheckHealthCommand()); + if (answer != null && answer.getResult()) { + final Status status = Status.Up; + logger.debug("Agent ({}) responded to checkHealthCommand, reporting that agent is {}", host, status); + return status; + } + return _haMgr.investigate(hostId); } protected AgentAttache getAttache(final Long hostId) throws AgentUnavailableException { @@ -714,15 +759,15 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } } - protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, final StartupCommand[] cmd, final boolean forRebalance) throws ConnectionException { + protected AgentAttache notifyMonitorsOfConnection(final AgentAttache attache, final StartupCommand[] cmds, final boolean forRebalance) throws ConnectionException { final long hostId = attache.getId(); final HostVO host = _hostDao.findById(hostId); for (final Pair monitor : _hostMonitors) { logger.debug("Sending Connect to listener: {}, for rebalance: {}", monitor.second().getClass().getSimpleName(), forRebalance); - for (int i = 0; i < cmd.length; i++) { + for (StartupCommand cmd : cmds) { try { - logger.debug("process connection to issue: {} for host: {}, forRebalance: {}, connection transferred: {}", ReflectionToStringBuilderUtils.reflectCollection(cmd[i]), hostId, forRebalance, cmd[i].isConnectionTransferred()); - monitor.second().processConnect(host, cmd[i], forRebalance); + logger.debug("process connection to issue: {} for host: {}, forRebalance: {}", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "id", "type", "msHostList", "connectionTransferred"), hostId, forRebalance); + monitor.second().processConnect(host, cmd, forRebalance); } catch (final ConnectionException ce) { if (ce.isSetupError()) { logger.warn("Monitor {} says there is an error in the connect process for {} due to {}", monitor.second().getClass().getSimpleName(), hostId, ce.getMessage()); @@ -758,11 +803,25 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl Map detailsMap = readyAnswer.getDetailsMap(); if (detailsMap != null) { String uefiEnabled = detailsMap.get(Host.HOST_UEFI_ENABLE); + String virtv2vVersion = detailsMap.get(Host.HOST_VIRTV2V_VERSION); + String ovftoolVersion = detailsMap.get(Host.HOST_OVFTOOL_VERSION); logger.debug("Got HOST_UEFI_ENABLE [{}] for host [{}]:", uefiEnabled, host); - if (uefiEnabled != null) { + if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion)) { _hostDao.loadDetails(host); - if (!uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) { + boolean updateNeeded = false; + if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) { host.getDetails().put(Host.HOST_UEFI_ENABLE, uefiEnabled); + updateNeeded = true; + } + if (StringUtils.isNotBlank(virtv2vVersion) && !virtv2vVersion.equals(host.getDetails().get(Host.HOST_VIRTV2V_VERSION))) { + host.getDetails().put(Host.HOST_VIRTV2V_VERSION, virtv2vVersion); + updateNeeded = true; + } + if (StringUtils.isNotBlank(ovftoolVersion) && !ovftoolVersion.equals(host.getDetails().get(Host.HOST_OVFTOOL_VERSION))) { + host.getDetails().put(Host.HOST_OVFTOOL_VERSION, ovftoolVersion); + updateNeeded = true; + } + if (updateNeeded) { _hostDao.saveDetails(host); } } @@ -779,9 +838,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl ManagementServerHostVO msHost = _mshostDao.findByMsid(_nodeId); if (msHost != null && (ManagementServerHost.State.Maintenance.equals(msHost.getState()) || ManagementServerHost.State.PreparingForMaintenance.equals(msHost.getState()))) { _monitorExecutor.shutdownNow(); + newAgentConnectionsMonitor.shutdownNow(); return true; } + initConnectExecutor(); startDirectlyConnectedHosts(false); if (_connection != null) { @@ -792,12 +853,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } } - _monitorExecutor.scheduleWithFixedDelay(new MonitorTask(), mgmtServiceConf.getPingInterval(), mgmtServiceConf.getPingInterval(), TimeUnit.SECONDS); - - final int cleanupTime = Wait.value(); - newAgentConnectionsMonitor.scheduleAtFixedRate(new AgentNewConnectionsMonitorTask(), cleanupTime, - cleanupTime, TimeUnit.MINUTES); - + initAndScheduleMonitorExecutor(); + initAndScheduleAgentConnectionsMonitor(); return true; } @@ -923,7 +980,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected AgentAttache createAttacheForDirectConnect(final Host host, final ServerResource resource) { logger.debug("create DirectAgentAttache for {}", host); - final DirectAgentAttache attache = new DirectAgentAttache(this, host.getId(), host.getUuid(), host.getName(), resource, host.isInMaintenanceStates()); + final DirectAgentAttache attache = new DirectAgentAttache(this, host.getId(), host.getUuid(), host.getName(), host.getHypervisorType(), resource, host.isInMaintenanceStates()); AgentAttache old; synchronized (_agents) { @@ -984,39 +1041,50 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected boolean handleDisconnectWithoutInvestigation(final AgentAttache attache, final Status.Event event, final boolean transitState, final boolean removeAgent) { final long hostId = attache.getId(); - + final HostVO host = _hostDao.findById(hostId); boolean result = false; GlobalLock joinLock = getHostJoinLock(hostId); - if (joinLock.lock(60)) { - try { - logger.info("Host {} is disconnecting with event {}", - attache, event); - Status nextStatus; - final HostVO host = _hostDao.findById(hostId); - if (host == null) { - logger.warn("Can't find host with {} ({})", hostId, attache); - nextStatus = Status.Removed; - } else { - nextStatus = getNextStatusOnDisconnection(host, event); - caService.purgeHostCertificate(host); - } - logger.debug("Deregistering link for {} with state {}", attache, nextStatus); - - removeAgent(attache, nextStatus); - - if (host != null && transitState) { - // update the state for host in DB as per the event - disconnectAgent(host, event, _nodeId); - } - } finally { - joinLock.unlock(); + try { + if (!joinLock.lock(60)) { + logger.debug("Unable to acquire lock on host {} to process agent disconnection", Objects.toString(host, String.valueOf(hostId))); + return result; } + + logger.debug("Acquired lock on host {}, to process agent disconnection", Objects.toString(host, String.valueOf(hostId))); + disconnectHostAgent(attache, event, host, transitState, joinLock); result = true; + } finally { + joinLock.releaseRef(); } - joinLock.releaseRef(); + return result; } + private void disconnectHostAgent(final AgentAttache attache, final Status.Event event, final HostVO host, final boolean transitState, final GlobalLock joinLock) { + try { + logger.info("Host {} is disconnecting with event {}", attache, event); + final long hostId = attache.getId(); + Status nextStatus; + if (host == null) { + logger.warn("Can't find host with {} ({})", hostId, attache); + nextStatus = Status.Removed; + } else { + nextStatus = getNextStatusOnDisconnection(host, event); + caService.purgeHostCertificate(host); + } + logger.debug("Deregistering link for {} with state {}", attache, nextStatus); + + removeAgent(attache, nextStatus); + + if (host != null && transitState) { + // update the state for host in DB as per the event + disconnectAgent(host, event, _nodeId); + } + } finally { + joinLock.unlock(); + } + } + protected boolean handleDisconnectWithInvestigation(final AgentAttache attache, Status.Event event) { final long hostId = attache.getId(); HostVO host = _hostDao.findById(hostId); @@ -1056,9 +1124,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl if (determinedState == Status.Down) { final String message = String.format("Host %s is down. Starting HA on the VMs", host); logger.error(message); - if (host.getType() != Host.Type.SecondaryStorage && host.getType() != Host.Type.ConsoleProxy) { - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), - host.getPodId(), String.format("Host down, %s", host), message); + if (Status.Down.equals(host.getStatus())) { + logger.debug(String.format("Skipping sending alert for %s as it already in %s state", + host, host.getStatus())); + } else if (!HOST_DOWN_ALERT_UNSUPPORTED_HOST_TYPES.contains(host.getType())) { + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "Host down, " + host.getId(), message); } event = Status.Event.HostDown; } else if (determinedState == Status.Up) { @@ -1106,6 +1176,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl host = _hostDao.findById(hostId); // Maybe the host magically reappeared? if (host != null && host.getStatus() == Status.Down) { _haMgr.scheduleRestartForVmsOnHost(host, true, HighAvailabilityManager.ReasonType.HostDown); + reconcileCommandService.updateReconcileCommandToInterruptedByHostId(hostId); } return true; } @@ -1268,7 +1339,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl protected AgentAttache createAttacheForConnect(final HostVO host, final Link link) { logger.debug("create ConnectedAgentAttache for {}", host); - final AgentAttache attache = new ConnectedAgentAttache(this, host.getId(), host.getUuid(), host.getName(), link, host.isInMaintenanceStates()); + final AgentAttache attache = new ConnectedAgentAttache(this, host.getId(), host.getUuid(), host.getName(), host.getHypervisorType(), link, host.isInMaintenanceStates()); link.attach(attache); AgentAttache old; @@ -1282,43 +1353,58 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl return attache; } - private AgentAttache sendReadyAndGetAttache(HostVO host, ReadyCommand ready, Link link, StartupCommand[] startup) throws ConnectionException { - final List agentMSHostList = new ArrayList<>(); - String lbAlgorithm = null; - if (startup != null && startup.length > 0) { - final String agentMSHosts = startup[0].getMsHostList(); - if (StringUtils.isNotEmpty(agentMSHosts)) { - String[] msHosts = agentMSHosts.split("@"); - if (msHosts.length > 1) { - lbAlgorithm = msHosts[1]; - } - agentMSHostList.addAll(Arrays.asList(msHosts[0].split(","))); - } - } - ready.setArch(host.getArch().getType()); + private AgentAttache sendReadyAndGetAttache(HostVO host, ReadyCommand ready, Link link, StartupCommand[] startupCmds) throws ConnectionException { AgentAttache attache; GlobalLock joinLock = getHostJoinLock(host.getId()); - if (joinLock.lock(60)) { - try { - - if (!indirectAgentLB.compareManagementServerList(host.getId(), host.getDataCenterId(), agentMSHostList, lbAlgorithm)) { - final List newMSList = indirectAgentLB.getManagementServerList(host.getId(), host.getDataCenterId(), null); - ready.setMsHostList(newMSList); - ready.setLbAlgorithm(indirectAgentLB.getLBAlgorithmName()); - ready.setLbCheckInterval(indirectAgentLB.getLBPreferredHostCheckInterval(host.getClusterId())); - logger.debug("Agent's management server host list is not up to date, sending list update: {}", newMSList); - } - - attache = createAttacheForConnect(host, link); - attache = notifyMonitorsOfConnection(attache, startup, false); - } finally { - joinLock.unlock(); + try { + if (!joinLock.lock(60)) { + throw new ConnectionException(true, String.format("Unable to acquire lock on host %s, to process agent connection", host)); } - } else { - throw new ConnectionException(true, - String.format("Unable to acquire lock on host %s", host)); + + logger.debug("Acquired lock on host {}, to process agent connection", host); + attache = connectHostAgent(host, ready, link, startupCmds, joinLock); + } finally { + joinLock.releaseRef(); } - joinLock.releaseRef(); + + return attache; + } + + private AgentAttache connectHostAgent(HostVO host, ReadyCommand ready, Link link, StartupCommand[] startupCmds, GlobalLock joinLock) throws ConnectionException { + AgentAttache attache; + try { + final List agentMSHostList = new ArrayList<>(); + String lbAlgorithm = null; + if (startupCmds != null && startupCmds.length > 0) { + final String agentMSHosts = startupCmds[0].getMsHostList(); + if (StringUtils.isNotEmpty(agentMSHosts)) { + String[] msHosts = agentMSHosts.split("@"); + if (msHosts.length > 1) { + lbAlgorithm = msHosts[1]; + } + agentMSHostList.addAll(Arrays.asList(msHosts[0].split(","))); + } + } + + if (!indirectAgentLB.compareManagementServerListAndLBAlgorithm(host.getId(), host.getDataCenterId(), agentMSHostList, lbAlgorithm)) { + final List newMSList = indirectAgentLB.getManagementServerList(host.getId(), host.getDataCenterId(), null); + ready.setMsHostList(newMSList); + String newLBAlgorithm = indirectAgentLB.getLBAlgorithmName(); + ready.setLbAlgorithm(newLBAlgorithm); + logger.debug("Agent's management server host list or lb algorithm is not up to date, sending list and algorithm update: {}, {}", newMSList, newLBAlgorithm); + } + + final List avoidMsList = _mshostDao.listNonUpStateMsIPs(); + ready.setAvoidMsHostList(avoidMsList); + ready.setLbCheckInterval(indirectAgentLB.getLBPreferredHostCheckInterval(host.getClusterId())); + ready.setArch(host.getArch().getType()); + + attache = createAttacheForConnect(host, link); + attache = notifyMonitorsOfConnection(attache, startupCmds, false); + } finally { + joinLock.unlock(); + } + return attache; } @@ -1473,7 +1559,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } if (!BooleanUtils.toBoolean(EnableKVMAutoEnableDisable.valueIn(host.getClusterId()))) { logger.debug("{} is disabled for the cluster {}, cannot process the health check result " + - "received for the host {}", EnableKVMAutoEnableDisable.key(), host.getClusterId(), host); + "received for {}", EnableKVMAutoEnableDisable.key(), host.getClusterId(), host); return; } @@ -1605,10 +1691,14 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl logger.debug("Not processing {} for agent id={}; can't find the host in the DB", PingRoutingCommand.class.getSimpleName(), cmdHostId); } } - if (host!= null && host.getStatus() != Status.Up && gatewayAccessible) { + if (host != null && host.getStatus() != Status.Up && gatewayAccessible) { requestStartupCommand = true; } - answer = new PingAnswer((PingCommand)cmd, requestStartupCommand); + final List avoidMsList = _mshostDao.listNonUpStateMsIPs(); + answer = new PingAnswer((PingCommand)cmd, avoidMsList, requestStartupCommand); + + // Add or update reconcile tasks + reconcileCommandService.processCommand(cmd, answer); } else if (cmd instanceof ReadyAnswer) { final HostVO host = _hostDao.findById(attache.getId()); if (host == null) { @@ -1620,7 +1710,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } } } catch (final Throwable th) { - logger.warn("Caught: ", th); + logger.error("Caught: ", th); answer = new Answer(cmd, false, th.getMessage()); } answers[i] = answer; @@ -1635,7 +1725,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl try { link.send(response.toBytes()); } catch (final ClosedChannelException e) { - logger.warn("Unable to send response because connection is closed: {}", response); + logger.error("Unable to send response because connection is closed: {}", response); } } @@ -1756,11 +1846,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl return false; } - private void disconnectInternal(final long hostId, final Status.Event event, final boolean invstigate) { + private void disconnectInternal(final long hostId, final Status.Event event, final boolean investigate) { final AgentAttache attache = findAttache(hostId); if (attache != null) { - if (!invstigate) { + if (!investigate) { disconnectWithoutInvestigation(attache, event); } else { disconnectWithInvestigation(attache, event); @@ -1927,27 +2017,21 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl @Override protected void runInContext() { logger.trace("Agent New Connections Monitor is started."); - final int cleanupTime = Wait.value(); + final int cleanupTime = RemoteAgentNewConnectionsMonitorInterval.value(); Set> entrySet = newAgentConnections.entrySet(); - long cutOff = System.currentTimeMillis() - (cleanupTime * 60 * 1000L); - if (logger.isDebugEnabled()) { - List expiredConnections = newAgentConnections.entrySet() - .stream() - .filter(e -> e.getValue() <= cutOff) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - logger.debug("Currently {} active new connections, of which {} have expired - {}", - entrySet.size(), - expiredConnections.size(), - StringUtils.join(expiredConnections)); - } - for (Map.Entry entry : entrySet) { - if (entry.getValue() <= cutOff) { - if (logger.isTraceEnabled()) { - logger.trace("Cleaning up new agent connection for {}", entry.getKey()); - } - newAgentConnections.remove(entry.getKey()); - } + long cutOff = System.currentTimeMillis() - (cleanupTime * 1000L); + List expiredConnections = newAgentConnections.entrySet() + .stream() + .filter(e -> e.getValue() <= cutOff) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + logger.debug("Currently {} active new connections, of which {} have expired - {}", + entrySet.size(), + expiredConnections.size(), + StringUtils.join(expiredConnections)); + for (String connection : expiredConnections) { + logger.trace("Cleaning up new agent connection for {}", connection); + newAgentConnections.remove(connection); } } } @@ -2028,7 +2112,8 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl public ConfigKey[] getConfigKeys() { return new ConfigKey[] { CheckTxnBeforeSending, Workers, Port, Wait, AlertWait, DirectAgentLoadSize, DirectAgentPoolSize, DirectAgentThreadCap, EnableKVMAutoEnableDisable, ReadyCommandWait, - GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections }; + GranularWaitTimeForCommands, RemoteAgentSslHandshakeTimeout, RemoteAgentMaxConcurrentNewConnections, + RemoteAgentNewConnectionsMonitorInterval }; } protected class SetHostParamsListener implements Listener { @@ -2067,6 +2152,7 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl params.put(Config.RouterAggregationCommandEachTimeout.toString(), _configDao.getValue(Config.RouterAggregationCommandEachTimeout.toString())); params.put(Config.MigrateWait.toString(), _configDao.getValue(Config.MigrateWait.toString())); params.put(NetworkOrchestrationService.TUNGSTEN_ENABLED.key(), String.valueOf(NetworkOrchestrationService.TUNGSTEN_ENABLED.valueIn(host.getDataCenterId()))); + params.put(ReconcileCommandService.ReconcileCommandsEnabled.key(), String.valueOf(_reconcileCommandsEnabled)); try { SetHostParamsCommand cmds = new SetHostParamsCommand(params); @@ -2146,11 +2232,43 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl } @Override - public boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long timeoutDurationInMs) { + public boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long timeoutDurationInMs, boolean excludeHostsInMaintenance) { return true; } private GlobalLock getHostJoinLock(Long hostId) { return GlobalLock.getInternLock(String.format("%s-%s", "Host-Join", hostId)); } + + public boolean isReconcileCommandsEnabled(HypervisorType hypervisorType) { + return _reconcileCommandsEnabled && ReconcileCommandService.SupportedHypervisorTypes.contains(hypervisorType); + } + + public void updateReconcileCommandsIfNeeded(long requestSeq, Command[] commands, Command.State state) { + if (!_reconcileCommandsEnabled) { + return; + } + for (Command command: commands) { + if (command.isReconcile()) { + reconcileCommandService.updateReconcileCommand(requestSeq, command, null, state, null); + } + } + } + + public Pair getStateAndAnswerOfReconcileCommand(long requestSeq, Command command) { + ReconcileCommandVO reconcileCommandVO = reconcileCommandDao.findCommand(requestSeq, command.toString()); + if (reconcileCommandVO == null) { + return null; + } + Command.State state = reconcileCommandVO.getStateByAgent(); + if (reconcileCommandVO.getAnswerName() == null || reconcileCommandVO.getAnswerInfo() == null) { + return new Pair<>(state, null); + } + Answer answer = ReconcileCommandUtils.parseAnswerFromAnswerInfo(reconcileCommandVO.getAnswerName(), reconcileCommandVO.getAnswerInfo()); + return new Pair<>(state, answer); + } + + public Integer getReconcileInterval() { + return _reconcileCommandInterval; + } } diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentAttache.java index e36b145c8bc..5e4ccfa67c6 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentAttache.java @@ -31,6 +31,7 @@ import com.cloud.agent.api.Command; import com.cloud.agent.transport.Request; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.nio.Link; public class ClusteredAgentAttache extends ConnectedAgentAttache implements Routable { @@ -44,14 +45,14 @@ public class ClusteredAgentAttache extends ConnectedAgentAttache implements Rout s_clusteredAgentMgr = agentMgr; } - public ClusteredAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name) { - super(agentMgr, id, uuid, name, null, false); + public ClusteredAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final Hypervisor.HypervisorType hypervisorType) { + super(agentMgr, id, uuid, name, hypervisorType, null, false); _forward = true; _transferRequests = new LinkedList(); } - public ClusteredAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final Link link, final boolean maintenance) { - super(agentMgr, id, uuid, name, link, maintenance); + public ClusteredAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final Hypervisor.HypervisorType hypervisorType, final Link link, final boolean maintenance) { + super(agentMgr, id, uuid, name, hypervisorType, link, maintenance); _forward = link == null; _transferRequests = new LinkedList(); } diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java index c667df5412e..c6448982803 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredAgentManagerImpl.java @@ -42,10 +42,13 @@ import javax.naming.ConfigurationException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; +import com.cloud.resource.ResourceState; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.extensions.command.ExtensionServerActionBaseCommand; +import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager; import org.apache.cloudstack.ha.dao.HAConfigDao; import org.apache.cloudstack.maintenance.ManagementServerMaintenanceManager; import org.apache.cloudstack.maintenance.command.BaseShutdownManagementServerHostCommand; @@ -60,6 +63,7 @@ import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.outofbandmanagement.dao.OutOfBandManagementDao; import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.cloudstack.utils.security.SSLUtils; +import org.apache.commons.collections.CollectionUtils; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CancelCommand; @@ -105,8 +109,6 @@ import com.cloud.utils.nio.Link; import com.cloud.utils.nio.Task; import com.google.gson.Gson; -import org.apache.commons.collections.CollectionUtils; - public class ClusteredAgentManagerImpl extends AgentManagerImpl implements ClusterManagerListener, ClusteredAgentRebalanceService { private static ScheduledExecutorService s_transferExecutor = Executors.newScheduledThreadPool(2, new NamedThreadFactory("Cluster-AgentRebalancingExecutor")); private final long rebalanceTimeOut = 300000; // 5 mins - after this time remove the agent from the transfer list @@ -146,16 +148,18 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust private ManagementServerMaintenanceManager managementServerMaintenanceManager; @Inject private DataCenterDao dcDao; + @Inject + ExtensionsManager extensionsManager; protected ClusteredAgentManagerImpl() { super(); } - protected final ConfigKey EnableLB = new ConfigKey<>(Boolean.class, "agent.lb.enabled", "Advanced", "false", "Enable agent load balancing between management server nodes", true); + protected final ConfigKey EnableLB = new ConfigKey<>(Boolean.class, "agent.lb.enabled", "Advanced", "false", "Enable direct agents load balancing between management server nodes", true); protected final ConfigKey ConnectedAgentThreshold = new ConfigKey<>(Double.class, "agent.load.threshold", "Advanced", "0.7", - "What percentage of the agents can be held by one management server before load balancing happens", true, EnableLB.key()); - protected final ConfigKey LoadSize = new ConfigKey<>(Integer.class, "direct.agent.load.size", "Advanced", "16", "How many agents to connect to in each round", true); - protected final ConfigKey ScanInterval = new ConfigKey<>(Integer.class, "direct.agent.scan.interval", "Advanced", "90", "Interval between scans to load agents", false, + "What percentage of the direct agents can be held by one management server before load balancing happens", true, EnableLB.key()); + protected final ConfigKey LoadSize = new ConfigKey<>(Integer.class, "direct.agent.load.size", "Advanced", "16", "How many direct agents to connect to in each round", true); + protected final ConfigKey ScanInterval = new ConfigKey<>(Integer.class, "direct.agent.scan.interval", "Advanced", "90", "Interval between scans to load direct agents", false, ConfigKey.Scope.Global, 1000); @Override @@ -210,7 +214,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust scanDirectAgentToLoad(); } - private void scanDirectAgentToLoad() { + protected void scanDirectAgentToLoad() { logger.trace("Begin scanning directly connected hosts"); // for agents that are self-managed, threshold to be considered as disconnected after pingtimeout @@ -231,11 +235,21 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust logger.info("{} is detected down, but we have a forward attache running, disconnect this one before launching the host", host); removeAgent(agentattache, Status.Disconnected); } else { - continue; + logger.debug("Host {} status is {} but has an AgentAttache which is not forForward, try to load directly", host, host.getStatus()); + Status hostStatus = investigate(agentattache); + if (Status.Up == hostStatus) { + /* Got ping response from host, bring it back */ + logger.info("After investigation, Agent for host {} is determined to be up and running", host); + agentStatusTransitTo(host, Event.Ping, _nodeId); + } else { + logger.debug("After investigation, AgentAttache is not null but host status is {}, try to load directly {}", hostStatus, host); + loadDirectlyConnectedHost(host, false); + } } + } else { + logger.debug("AgentAttache is null, loading directly connected {}", host); + loadDirectlyConnectedHost(host, false); } - logger.debug("Loading directly connected {}", host); - loadDirectlyConnectedHost(host, false); } catch (final Throwable e) { logger.warn(" can not load directly connected {} due to ", host, e); } @@ -264,7 +278,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust protected AgentAttache createAttache(final HostVO host) { logger.debug("create forwarding ClusteredAgentAttache for {}", host); long id = host.getId(); - final AgentAttache attache = new ClusteredAgentAttache(this, id, host.getUuid(), host.getName()); + final AgentAttache attache = new ClusteredAgentAttache(this, id, host.getUuid(), host.getName(), host.getHypervisorType()); AgentAttache old; synchronized (_agents) { old = _agents.get(host.getId()); @@ -280,7 +294,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust @Override protected AgentAttache createAttacheForConnect(final HostVO host, final Link link) { logger.debug("create ClusteredAgentAttache for {}", host); - final AgentAttache attache = new ClusteredAgentAttache(this, host.getId(), host.getUuid(), host.getName(), link, host.isInMaintenanceStates()); + final AgentAttache attache = new ClusteredAgentAttache(this, host.getId(), host.getUuid(), host.getName(), host.getHypervisorType(), link, host.isInMaintenanceStates()); link.attach(attache); AgentAttache old; synchronized (_agents) { @@ -296,7 +310,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust @Override protected AgentAttache createAttacheForDirectConnect(final Host host, final ServerResource resource) { logger.debug("Create ClusteredDirectAgentAttache for {}.", host); - final DirectAgentAttache attache = new ClusteredDirectAgentAttache(this, host.getId(), host.getUuid(), host.getName(), _nodeId, resource, host.isInMaintenanceStates()); + final DirectAgentAttache attache = new ClusteredDirectAgentAttache(this, host.getId(), host.getUuid(), host.getName(), host.getHypervisorType(), _nodeId, resource, host.isInMaintenanceStates()); AgentAttache old; synchronized (_agents) { old = _agents.get(host.getId()); @@ -381,20 +395,20 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust return; } if (!result) { - throw new CloudRuntimeException("Failed to propagate agent change request event:" + Event.ShutdownRequested + " to host:" + hostId); + throw new CloudRuntimeException(String.format("Failed to propagate agent change request event: %s to host: %s", Event.ShutdownRequested, hostId)); } } public void notifyNodesInCluster(final AgentAttache attache) { logger.debug("Notifying other nodes of to disconnect"); - final Command[] cmds = new Command[] {new ChangeAgentCommand(attache.getId(), Event.AgentDisconnected)}; + final Command[] cmds = new Command[]{new ChangeAgentCommand(attache.getId(), Event.AgentDisconnected)}; _clusterMgr.broadcast(attache.getId(), _gson.toJson(cmds)); } // notifies MS peers to schedule a host scan task immediately, triggered during addHost operation public void notifyNodesInClusterToScheduleHostScanTask() { logger.debug("Notifying other MS nodes to run host scan task"); - final Command[] cmds = new Command[] {new ScheduleHostScanTaskCommand()}; + final Command[] cmds = new Command[]{new ScheduleHostScanTaskCommand()}; _clusterMgr.broadcast(0, _gson.toJson(cmds)); } @@ -421,10 +435,10 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust ch = connectToPeer(peer, ch); if (ch == null) { try { - logD(bytes, "Unable to route to peer: " + Request.parse(bytes)); + logD(bytes, "Unable to establish connection to route to peer: " + Request.parse(bytes)); } catch (ClassNotFoundException | UnsupportedVersionException e) { // Request.parse thrown exception when we try to log it, log as much as we can - logD(bytes, "Unable to route to peer, and Request.parse further caught exception" + e.getMessage()); + logD(bytes, "Unable to establish connection to route to peer, and Request.parse further caught exception" + e.getMessage()); } return false; } @@ -435,7 +449,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } try { logD(bytes, "Routing to peer"); - Link.write(ch, new ByteBuffer[] {ByteBuffer.wrap(bytes)}, sslEngine); + Link.write(ch, new ByteBuffer[]{ByteBuffer.wrap(bytes)}, sslEngine); return true; } catch (final IOException e) { try { @@ -633,7 +647,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust final Link link = task.getLink(); if (Request.fromServer(data)) { - final AgentAttache agent = findAttache(hostId); if (Request.isControl(data)) { @@ -681,7 +694,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust cancel(Long.toString(Request.getManagementServerId(data)), hostId, Request.getSequence(data), e.getMessage()); } } else { - final long mgmtId = Request.getManagementServerId(data); if (mgmtId != -1 && mgmtId != _nodeId) { routeToPeer(Long.toString(mgmtId), data); @@ -954,7 +966,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust if (!_agentToTransferIds.isEmpty()) { logger.debug("Found {} agents to transfer", _agentToTransferIds.size()); // for (Long hostId : _agentToTransferIds) { - for (final Iterator iterator = _agentToTransferIds.iterator(); iterator.hasNext();) { + for (final Iterator iterator = _agentToTransferIds.iterator(); iterator.hasNext(); ) { final Long hostId = iterator.next(); final AgentAttache attache = findAttache(hostId); @@ -1095,7 +1107,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust return; } - final ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)attache; + final ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache) attache; if (success) { @@ -1146,10 +1158,10 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } synchronized (_agents) { - final ClusteredDirectAgentAttache attache = (ClusteredDirectAgentAttache)_agents.get(hostId); + final ClusteredDirectAgentAttache attache = (ClusteredDirectAgentAttache) _agents.get(hostId); if (attache != null && attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) { handleDisconnectWithoutInvestigation(attache, Event.StartAgentRebalance, true, true); - final ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)createAttache(host); + final ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache) createAttache(host); if (forwardAttache == null) { logger.warn("Unable to create a forward attache for the host {} as a part of rebalance process", host); return false; @@ -1253,7 +1265,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } if (cmds.length == 1 && cmds[0] instanceof ChangeAgentCommand) { // intercepted - final ChangeAgentCommand cmd = (ChangeAgentCommand)cmds[0]; + final ChangeAgentCommand cmd = (ChangeAgentCommand) cmds[0]; logger.debug("Intercepting command for agent change: agent {} event: {}", cmd.getAgentId(), cmd.getEvent()); boolean result; @@ -1270,7 +1282,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust answers[0] = new ChangeAgentAnswer(cmd, result); return _gson.toJson(answers); } else if (cmds.length == 1 && cmds[0] instanceof TransferAgentCommand) { - final TransferAgentCommand cmd = (TransferAgentCommand)cmds[0]; + final TransferAgentCommand cmd = (TransferAgentCommand) cmds[0]; logger.debug("Intercepting command for agent rebalancing: agent: {}, event: {}, connection transfer: {}", cmd.getAgentId(), cmd.getEvent(), cmd.isConnectionTransfer()); boolean result; @@ -1289,7 +1301,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust answers[0] = new Answer(cmd, result, null); return _gson.toJson(answers); } else if (cmds.length == 1 && cmds[0] instanceof PropagateResourceEventCommand) { - final PropagateResourceEventCommand cmd = (PropagateResourceEventCommand)cmds[0]; + final PropagateResourceEventCommand cmd = (PropagateResourceEventCommand) cmds[0]; logger.debug("Intercepting command to propagate event {} for host {} ({})", () -> cmd.getEvent().name(), cmd::getHostId, () -> _hostDao.findById(cmd.getHostId())); @@ -1306,11 +1318,13 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust answers[0] = new Answer(cmd, result, null); return _gson.toJson(answers); } else if (cmds.length == 1 && cmds[0] instanceof ScheduleHostScanTaskCommand) { - final ScheduleHostScanTaskCommand cmd = (ScheduleHostScanTaskCommand)cmds[0]; + final ScheduleHostScanTaskCommand cmd = (ScheduleHostScanTaskCommand) cmds[0]; return handleScheduleHostScanTaskCommand(cmd); } else if (cmds.length == 1 && cmds[0] instanceof BaseShutdownManagementServerHostCommand) { - final BaseShutdownManagementServerHostCommand cmd = (BaseShutdownManagementServerHostCommand)cmds[0]; + final BaseShutdownManagementServerHostCommand cmd = (BaseShutdownManagementServerHostCommand) cmds[0]; return handleShutdownManagementServerHostCommand(cmd); + } else if (cmds.length == 1 && cmds[0] instanceof ExtensionServerActionBaseCommand) { + return extensionsManager.handleExtensionServerCommands((ExtensionServerActionBaseCommand)cmds[0]); } try { @@ -1342,7 +1356,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust if (cmd instanceof PrepareForMaintenanceManagementServerHostCommand) { logger.debug("Received PrepareForMaintenanceManagementServerHostCommand - preparing for maintenance"); try { - managementServerMaintenanceManager.prepareForMaintenance(((PrepareForMaintenanceManagementServerHostCommand) cmd).getLbAlgorithm()); + managementServerMaintenanceManager.prepareForMaintenance(((PrepareForMaintenanceManagementServerHostCommand) cmd).getLbAlgorithm(), ((PrepareForMaintenanceManagementServerHostCommand) cmd).isForced()); return "Successfully prepared for maintenance"; } catch(CloudRuntimeException e) { return e.getMessage(); @@ -1362,7 +1376,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust try { managementServerMaintenanceManager.prepareForShutdown(); return "Successfully prepared for shutdown"; - } catch(CloudRuntimeException e) { + } catch (CloudRuntimeException e) { return e.getMessage(); } } @@ -1371,7 +1385,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust try { managementServerMaintenanceManager.triggerShutdown(); return "Successfully triggered shutdown"; - } catch(CloudRuntimeException e) { + } catch (CloudRuntimeException e) { return e.getMessage(); } } @@ -1380,7 +1394,7 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust try { managementServerMaintenanceManager.cancelShutdown(); return "Successfully cancelled shutdown"; - } catch(CloudRuntimeException e) { + } catch (CloudRuntimeException e) { return e.getMessage(); } } @@ -1389,14 +1403,14 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } @Override - public boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long timeoutDurationInMs) { + public boolean transferDirectAgentsFromMS(String fromMsUuid, long fromMsId, long timeoutDurationInMs, boolean excludeHostsInMaintenance) { if (timeoutDurationInMs <= 0) { logger.debug("Not transferring direct agents from management server node {} (id: {}) to other nodes, invalid timeout duration", fromMsId, fromMsUuid); return false; } - long transferStartTime = System.currentTimeMillis(); - if (CollectionUtils.isEmpty(getDirectAgentHosts(fromMsId))) { + long transferStartTimeInMs = System.currentTimeMillis(); + if (CollectionUtils.isEmpty(getDirectAgentHosts(fromMsId, excludeHostsInMaintenance))) { logger.info("No direct agent hosts available on management server node {} (id: {}), to transfer", fromMsId, fromMsUuid); return true; } @@ -1411,13 +1425,13 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust int agentTransferFailedCount = 0; List dataCenterList = dcDao.listAll(); for (DataCenterVO dc : dataCenterList) { - List directAgentHostsInDc = getDirectAgentHostsInDc(fromMsId, dc.getId()); + List directAgentHostsInDc = getDirectAgentHostsInDc(fromMsId, dc.getId(), excludeHostsInMaintenance); if (CollectionUtils.isEmpty(directAgentHostsInDc)) { continue; } logger.debug("Transferring {} direct agents from management server node {} (id: {}) of zone {}", directAgentHostsInDc.size(), fromMsId, fromMsUuid, dc); for (HostVO host : directAgentHostsInDc) { - long transferElapsedTimeInMs = System.currentTimeMillis() - transferStartTime; + long transferElapsedTimeInMs = System.currentTimeMillis() - transferStartTimeInMs; if (transferElapsedTimeInMs >= timeoutDurationInMs) { logger.debug("Stop transferring remaining direct agents from management server node {} (id: {}), timed out", fromMsId, fromMsUuid); return false; @@ -1445,9 +1459,10 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust return (agentTransferFailedCount == 0); } - private List getDirectAgentHosts(long msId) { + private List getDirectAgentHosts(long msId, boolean excludeHostsInMaintenance) { List directAgentHosts = new ArrayList<>(); - List hosts = _hostDao.listHostsByMs(msId); + List statesToExclude = excludeHostsInMaintenance ? ResourceState.s_maintenanceStates : List.of(); + List hosts = _hostDao.listHostsByMsResourceState(msId, statesToExclude); for (HostVO host : hosts) { AgentAttache agent = findAttache(host.getId()); if (agent instanceof DirectAgentAttache) { @@ -1458,9 +1473,11 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust return directAgentHosts; } - private List getDirectAgentHostsInDc(long msId, long dcId) { + private List getDirectAgentHostsInDc(long msId, long dcId, boolean excludeHostsInMaintenance) { List directAgentHosts = new ArrayList<>(); - List hosts = _hostDao.listHostsByMsAndDc(msId, dcId); + // To exclude maintenance states use values from ResourceState as source of truth + List statesToExclude = excludeHostsInMaintenance ? ResourceState.s_maintenanceStates : List.of(); + List hosts = _hostDao.listHostsByMsDcResourceState(msId, dcId, statesToExclude); for (HostVO host : hosts) { AgentAttache agent = findAttache(host.getId()); if (agent instanceof DirectAgentAttache) { @@ -1486,6 +1503,22 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust } } + @Override + public void onManagementServerPreparingForMaintenance() { + logger.debug("Management server preparing for maintenance"); + super.onManagementServerPreparingForMaintenance(); + } + + @Override + public void onManagementServerCancelPreparingForMaintenance() { + logger.debug("Management server cancel preparing for maintenance"); + super.onManagementServerPreparingForMaintenance(); + + // needed for the case when Management Server in Preparing For Maintenance but didn't go to Maintenance state + // (where this variable will be reset) + _agentLbHappened = false; + } + @Override public void onManagementServerMaintenance() { logger.debug("Management server maintenance enabled"); diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredDirectAgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredDirectAgentAttache.java index e36ea6cedc1..5e796d8e0d8 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredDirectAgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ClusteredDirectAgentAttache.java @@ -20,14 +20,15 @@ import com.cloud.agent.transport.Request; import com.cloud.agent.transport.Response; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.UnsupportedVersionException; +import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ServerResource; import com.cloud.utils.exception.CloudRuntimeException; public class ClusteredDirectAgentAttache extends DirectAgentAttache implements Routable { private final long _nodeId; - public ClusteredDirectAgentAttache(ClusteredAgentManagerImpl agentMgr, long id, String uuid, String name, long mgmtId, ServerResource resource, boolean maintenance) { - super(agentMgr, id, uuid, name, resource, maintenance); + public ClusteredDirectAgentAttache(ClusteredAgentManagerImpl agentMgr, long id, String uuid, String name, final Hypervisor.HypervisorType hypervisorType, long mgmtId, ServerResource resource, boolean maintenance) { + super(agentMgr, id, uuid, name, hypervisorType, resource, maintenance); _nodeId = mgmtId; } diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/ConnectedAgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/ConnectedAgentAttache.java index 523f98fd010..f4efaaa34a4 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/ConnectedAgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/ConnectedAgentAttache.java @@ -22,6 +22,7 @@ import java.nio.channels.ClosedChannelException; import com.cloud.agent.transport.Request; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.nio.Link; /** @@ -31,8 +32,8 @@ public class ConnectedAgentAttache extends AgentAttache { protected Link _link; - public ConnectedAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final Link link, final boolean maintenance) { - super(agentMgr, id, uuid, name, maintenance); + public ConnectedAgentAttache(final AgentManagerImpl agentMgr, final long id, final String uuid, final String name, final Hypervisor.HypervisorType hypervisorType, final Link link, final boolean maintenance) { + super(agentMgr, id, uuid, name, hypervisorType, maintenance); _link = link; } @@ -53,8 +54,10 @@ public class ConnectedAgentAttache extends AgentAttache { @Override public void disconnect(final Status state) { synchronized (this) { - logger.debug("Processing Disconnect."); + logger.debug("Processing disconnect [id: {}, uuid: {}, name: {}]", _id, _uuid, _name); + if (_link != null) { + logger.debug("Disconnecting from {}, Socket Address: {}", _link.getIpAddress(), _link.getSocketAddress()); _link.close(); _link.terminated(); } diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/DirectAgentAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/DirectAgentAttache.java index 07d5bf80393..ed18d1e82b7 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/DirectAgentAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/DirectAgentAttache.java @@ -23,6 +23,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import com.cloud.utils.ThreadUtil; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.managed.context.ManagedContextRunnable; @@ -35,6 +36,7 @@ import com.cloud.agent.transport.Request; import com.cloud.agent.transport.Response; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ServerResource; import org.apache.logging.log4j.ThreadContext; @@ -51,8 +53,8 @@ public class DirectAgentAttache extends AgentAttache { AtomicInteger _outstandingTaskCount; AtomicInteger _outstandingCronTaskCount; - public DirectAgentAttache(AgentManagerImpl agentMgr, long id, String uuid,String name, ServerResource resource, boolean maintenance) { - super(agentMgr, id, uuid, name, maintenance); + public DirectAgentAttache(AgentManagerImpl agentMgr, long id, String uuid, String name, final Hypervisor.HypervisorType hypervisorType, ServerResource resource, boolean maintenance) { + super(agentMgr, id, uuid, name, hypervisorType, maintenance); _resource = resource; _outstandingTaskCount = new AtomicInteger(0); _outstandingCronTaskCount = new AtomicInteger(0); @@ -165,7 +167,7 @@ public class DirectAgentAttache extends AgentAttache { PingCommand cmd = resource.getCurrentStatus(_id); int retried = 0; while (cmd == null && ++retried <= _HostPingRetryCount.value()) { - Thread.sleep(1000*_HostPingRetryTimer.value()); + ThreadUtil.wait(this, 1000L *_HostPingRetryTimer.value(), _id, _uuid, _name); cmd = resource.getCurrentStatus(_id); } diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/DummyAttache.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/DummyAttache.java index 2f15e7af43c..f2b253fffcb 100644 --- a/engine/orchestration/src/main/java/com/cloud/agent/manager/DummyAttache.java +++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/DummyAttache.java @@ -19,11 +19,12 @@ package com.cloud.agent.manager; import com.cloud.agent.transport.Request; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.Status; +import com.cloud.hypervisor.Hypervisor; public class DummyAttache extends AgentAttache { - public DummyAttache(AgentManagerImpl agentMgr, long id, String uuid, String name, boolean maintenance) { - super(agentMgr, id, uuid, name, maintenance); + public DummyAttache(AgentManagerImpl agentMgr, long id, String uuid, String name, final Hypervisor.HypervisorType hypervisorType, boolean maintenance) { + super(agentMgr, id, uuid, name, hypervisorType, maintenance); } @Override diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 6d27b0efed3..3a6e1b62277 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -49,6 +49,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; + import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -57,15 +58,25 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd; import org.apache.cloudstack.api.command.admin.volume.MigrateVolumeCmdByAdmin; import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd; +import org.apache.cloudstack.backup.BackupManager; +import org.apache.cloudstack.backup.dao.BackupDao; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.framework.ca.Certificate; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.extensions.dao.ExtensionDetailsDao; +import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager; +import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; import org.apache.cloudstack.framework.jobs.AsyncJobManager; @@ -78,11 +89,13 @@ import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.MessageDispatcher; import org.apache.cloudstack.framework.messagebus.MessageHandler; +import org.apache.cloudstack.gpu.GpuService; import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.resource.ResourceCleanupService; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.utils.cache.SingleCache; @@ -92,6 +105,7 @@ import org.apache.cloudstack.vm.UnmanagedVMsManager; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -116,10 +130,13 @@ import com.cloud.agent.api.ModifyTargetsCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PlugNicAnswer; import com.cloud.agent.api.PlugNicCommand; +import com.cloud.agent.api.PrepareExternalProvisioningAnswer; +import com.cloud.agent.api.PrepareExternalProvisioningCommand; import com.cloud.agent.api.PrepareForMigrationAnswer; import com.cloud.agent.api.PrepareForMigrationCommand; import com.cloud.agent.api.RebootAnswer; import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.RecreateCheckpointsCommand; import com.cloud.agent.api.ReplugNicAnswer; import com.cloud.agent.api.ReplugNicCommand; import com.cloud.agent.api.RestoreVMSnapshotAnswer; @@ -195,12 +212,14 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuruBase; import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.network.Network; import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkService; import com.cloud.network.Networks; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkDetailVO; @@ -223,6 +242,7 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; +import com.cloud.storage.Snapshot; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageManager; @@ -241,6 +261,7 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.ResourceLimitService; @@ -276,13 +297,14 @@ import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.VMSnapshotManager; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; import com.google.gson.Gson; + public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable { public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName(); @@ -324,6 +346,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private HostDao _hostDao; @Inject + private HostDetailsDao hostDetailsDao; + @Inject private AlertManager _alertMgr; @Inject private GuestOSCategoryDao _guestOsCategoryDao; @@ -370,10 +394,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private ClusterDetailsDao _clusterDetailsDao; @Inject - private UserVmDetailsDao userVmDetailsDao; + private VMInstanceDetailsDao vmInstanceDetailsDao; @Inject private VolumeOrchestrationService volumeMgr; @Inject + private GpuService gpuService; + @Inject private DeploymentPlanningManager _dpMgr; @Inject private MessageBus _messageBus; @@ -406,12 +432,34 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private DomainDao domainDao; @Inject + public NetworkService networkService; + @Inject ResourceCleanupService resourceCleanupService; @Inject VmWorkJobDao vmWorkJobDao; + @Inject + DataStoreProviderManager dataStoreProviderManager; + @Inject + BackupManager backupManager; + @Inject + BackupDao backupDao; private SingleCache> vmIdsInProgressCache; + @Inject + private SnapshotDataStoreDao snapshotDataStoreDao; + + @Inject + private SnapshotManager snapshotManager; + + @Inject + private VolumeDataFactory volumeDataFactory; + @Inject + ExtensionsManager extensionsManager; + @Inject + ExtensionDetailsDao extensionDetailsDao; + + VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); Map _vmGurus = new HashMap<>(); @@ -427,7 +475,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac static final ConfigKey VmOpCleanupInterval = new ConfigKey("Advanced", Long.class, "vm.op.cleanup.interval", "86400", "Interval to run the thread that cleans up the vm operations (in seconds)", false); static final ConfigKey VmOpCleanupWait = new ConfigKey("Advanced", Long.class, "vm.op.cleanup.wait", "3600", - "Time (in seconds) to wait before cleanuping up any vm work items", true); + "Time (in seconds) to wait before cleaning up any vm work items", true); static final ConfigKey VmOpCancelInterval = new ConfigKey("Advanced", Long.class, "vm.op.cancel.interval", "3600", "Time (in seconds) to wait before cancelling a operation", false); static final ConfigKey VmDestroyForcestop = new ConfigKey("Advanced", Boolean.class, "vm.destroy.forcestop", "false", @@ -483,8 +531,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override @DB public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering, - final DiskOfferingInfo rootDiskOfferingInfo, final List dataDiskOfferings, - final LinkedHashMap> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType, final Map> extraDhcpOptions, final Map datadiskTemplateToDiskOfferingMap) + final DiskOfferingInfo rootDiskOfferingInfo, final List dataDiskOfferings, List dataDiskDeviceIds, + final LinkedHashMap> auxiliaryNetworks,final DeploymentPlan plan, final HypervisorType hyperType, + final Map> extraDhcpOptions, final Map datadiskTemplateToDiskOfferingMap, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { logger.info("allocating virtual machine from template: {} with hostname: {} and {} networks", template, vmInstanceName, auxiliaryNetworks.size()); @@ -522,25 +571,28 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac logger.debug("Allocating disks for {}", persistedVm); - allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal); + allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal, volume, snapshot); // Create new Volume context and inject event resource type, id and details to generate VOLUME.CREATE event for the ROOT disk. CallContext volumeContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Volume); try { if (dataDiskOfferings != null) { + int index = 0; for (final DiskOfferingInfo dataDiskOfferingInfo : dataDiskOfferings) { - volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId(), dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(), - dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, null); + Long deviceId = dataDiskDeviceIds.get(index++); + String volumeName = deviceId == null ? "DATA-" + persistedVm.getId() : "DATA-" + persistedVm.getId() + "-" + String.valueOf(deviceId); + volumeMgr.allocateRawVolume(Type.DATADISK, volumeName, dataDiskOfferingInfo.getDiskOffering(), dataDiskOfferingInfo.getSize(), + dataDiskOfferingInfo.getMinIops(), dataDiskOfferingInfo.getMaxIops(), persistedVm, template, owner, deviceId); } } if (datadiskTemplateToDiskOfferingMap != null && !datadiskTemplateToDiskOfferingMap.isEmpty()) { - int diskNumber = 1; + Long diskNumber = 1L; for (Entry dataDiskTemplateToDiskOfferingMap : datadiskTemplateToDiskOfferingMap.entrySet()) { DiskOffering diskOffering = dataDiskTemplateToDiskOfferingMap.getValue(); long diskOfferingSize = diskOffering.getDiskSize() / (1024 * 1024 * 1024); VMTemplateVO dataDiskTemplate = _templateDao.findById(dataDiskTemplateToDiskOfferingMap.getKey()); - volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf(diskNumber), diskOffering, diskOfferingSize, null, null, - persistedVm, dataDiskTemplate, owner, Long.valueOf(diskNumber)); + volumeMgr.allocateRawVolume(Type.DATADISK, "DATA-" + persistedVm.getId() + "-" + String.valueOf( diskNumber), diskOffering, diskOfferingSize, null, null, + persistedVm, dataDiskTemplate, owner, diskNumber); diskNumber++; } } @@ -563,7 +615,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template, DiskOfferingInfo rootDiskOfferingInfo, Account owner, Long rootDiskSizeFinal) { + private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template, DiskOfferingInfo rootDiskOfferingInfo, Account owner, Long rootDiskSizeFinal, Volume volume, Snapshot snapshot) { // Create new Volume context and inject event resource type, id and details to generate VOLUME.CREATE event for the ROOT disk. CallContext volumeContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Volume); try { @@ -571,11 +623,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (template.getFormat() == ImageFormat.ISO) { volumeMgr.allocateRawVolume(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(), rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), vm, template, owner, null); - } else if (template.getFormat() == ImageFormat.BAREMETAL) { - logger.debug("%s has format [{}]. Skipping ROOT volume [{}] allocation.", template.toString(), ImageFormat.BAREMETAL, rootVolumeName); + } else if (Arrays.asList(ImageFormat.BAREMETAL, ImageFormat.EXTERNAL).contains(template.getFormat())) { + logger.debug("{} has format [{}]. Skipping ROOT volume [{}] allocation.", template, template.getFormat(), rootVolumeName); } else { volumeMgr.allocateTemplatedVolumes(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskSizeFinal, - rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner); + rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner, volume, snapshot); } } finally { // Remove volumeContext and pop vmContext back @@ -585,9 +637,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering, - final LinkedHashMap> networks, final DeploymentPlan plan, final HypervisorType hyperType) throws InsufficientCapacityException { + final LinkedHashMap> networks, final DeploymentPlan plan, final HypervisorType hyperType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { DiskOffering diskOffering = _diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); - allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), networks, plan, hyperType, null, null); + allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), new ArrayList<>(), networks, plan, hyperType, null, null, volume, snapshot); } VirtualMachineGuru getVmGuru(final VirtualMachine vm) { @@ -632,6 +684,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return; } + if (HypervisorType.External.equals(vm.getHypervisorType())) { + UserVmVO userVM = _userVmDao.findById(vm.getId()); + _userVmDao.loadDetails(userVM); + userVM.setDetail(VmDetailConstants.EXPUNGE_EXTERNAL_VM, Boolean.TRUE.toString()); + _userVmDao.saveDetails(userVM); + } + advanceStop(vm.getUuid(), VmDestroyForcestop.value()); vm = _vmDao.findByUuid(vm.getUuid()); @@ -707,7 +766,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private void handleUnsuccessfulExpungeOperation(List finalizeExpungeCommands, List nicExpungeCommands, VMInstanceVO vm, Long hostId) throws OperationTimedoutException, AgentUnavailableException { - if (CollectionUtils.isNotEmpty(finalizeExpungeCommands) || CollectionUtils.isNotEmpty(nicExpungeCommands) && (hostId != null)) { + if ((CollectionUtils.isNotEmpty(finalizeExpungeCommands) || CollectionUtils.isNotEmpty(nicExpungeCommands)) && hostId != null) { final Commands cmds = new Commands(Command.OnError.Stop); addAllExpungeCommandsFromList(finalizeExpungeCommands, cmds, vm); addAllExpungeCommandsFromList(nicExpungeCommands, cmds, vm); @@ -1124,6 +1183,141 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac vmTO.setMetadataProductName(metadataProduct); } + protected void updateExternalVmDetailsFromPrepareAnswer(VirtualMachineTO vmTO, UserVmVO userVmVO, + Map newDetails) { + if (newDetails == null || newDetails.equals(vmTO.getDetails())) { + return; + } + vmTO.setDetails(newDetails); + userVmVO.setDetails(newDetails); + _userVmDao.saveDetails(userVmVO); + } + + protected void updateExternalVmDataFromPrepareAnswer(VirtualMachineTO vmTO, VirtualMachineTO updatedTO) { + final String vncPassword = updatedTO.getVncPassword(); + final Map details = updatedTO.getDetails(); + if ((vncPassword == null || vncPassword.equals(vmTO.getVncPassword())) && + (details == null || details.equals(vmTO.getDetails()))) { + return; + } + UserVmVO userVmVO = _userVmDao.findById(vmTO.getId()); + if (userVmVO == null) { + return; + } + if (vncPassword != null && !vncPassword.equals(userVmVO.getPassword())) { + userVmVO.setVncPassword(vncPassword); + vmTO.setVncPassword(vncPassword); + } + updateExternalVmDetailsFromPrepareAnswer(vmTO, userVmVO, updatedTO.getDetails()); + } + + protected void updateExternalVmNicsFromPrepareAnswer(VirtualMachineTO vmTO, VirtualMachineTO updatedTO) { + if (ObjectUtils.anyNull(vmTO.getNics(), updatedTO.getNics())) { + return; + } + Map originalNicsByUuid = new HashMap<>(); + for (NicTO nic : vmTO.getNics()) { + originalNicsByUuid.put(nic.getNicUuid(), nic); + } + for (NicTO updatedNicTO : updatedTO.getNics()) { + final String nicUuid = updatedNicTO.getNicUuid(); + NicTO originalNicTO = originalNicsByUuid.get(nicUuid); + if (originalNicTO == null) { + continue; + } + final String mac = updatedNicTO.getMac(); + final String ip4 = updatedNicTO.getIp(); + final String ip6 = updatedNicTO.getIp6Address(); + if (Objects.equals(mac, originalNicTO.getMac()) && + Objects.equals(ip4, originalNicTO.getIp()) && + Objects.equals(ip6, originalNicTO.getIp6Address())) { + continue; + } + NicVO nicVO = _nicsDao.findByUuid(nicUuid); + if (nicVO == null) { + continue; + } + logger.debug("Updating {} during External VM preparation", nicVO); + if (ip4 != null && !ip4.equals(nicVO.getIPv4Address())) { + nicVO.setIPv4Address(ip4); + originalNicTO.setIp(ip4); + } + if (ip6 != null && !ip6.equals(nicVO.getIPv6Address())) { + nicVO.setIPv6Address(ip6); + originalNicTO.setIp6Address(ip6); + } + if (mac != null && !mac.equals(nicVO.getMacAddress())) { + nicVO.setMacAddress(mac); + originalNicTO.setMac(mac); + } + _nicsDao.update(nicVO.getId(), nicVO); + } + } + + protected void updateExternalVmFromPrepareAnswer(VirtualMachineTO vmTO, VirtualMachineTO updatedTO) { + if (updatedTO == null) { + return; + } + updateExternalVmDataFromPrepareAnswer(vmTO, updatedTO); + updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + return; + } + + protected void processPrepareExternalProvisioning(boolean firstStart, Host host, + VirtualMachineProfile vmProfile, DataCenter dataCenter) throws CloudRuntimeException { + VirtualMachineTemplate template = vmProfile.getTemplate(); + if (!firstStart || host == null || !HypervisorType.External.equals(host.getHypervisorType()) || + template.getExtensionId() == null) { + return; + } + ExtensionDetailsVO detailsVO = extensionDetailsDao.findDetail(template.getExtensionId(), + ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM); + if (detailsVO == null || !Boolean.parseBoolean(detailsVO.getValue())) { + return; + } + logger.debug("Sending PrepareExternalProvisioningCommand for {}", vmProfile); + VirtualMachineTO virtualMachineTO = toVmTO(vmProfile); + if (virtualMachineTO.getNics() == null || virtualMachineTO.getNics().length == 0) { + List nics = _nicsDao.listByVmId(vmProfile.getId()); + NicTO[] nicTOs = new NicTO[nics.size()]; + nics.forEach(nicVO -> { + NicTO nicTO = toNicTO(_networkModel.getNicProfile(vmProfile.getVirtualMachine(), nicVO, dataCenter), + HypervisorType.External); + nicTOs[nicTO.getDeviceId()] = nicTO; + }); + virtualMachineTO.setNics(nicTOs); + } + Map vmDetails = virtualMachineTO.getExternalDetails(); + Map> externalDetails = extensionsManager.getExternalAccessDetails(host, + vmDetails); + PrepareExternalProvisioningCommand cmd = new PrepareExternalProvisioningCommand(virtualMachineTO); + cmd.setExternalDetails(externalDetails); + Answer answer = null; + CloudRuntimeException cre = new CloudRuntimeException("Failed to prepare VM"); + try { + answer = _agentMgr.send(host.getId(), cmd); + } catch (AgentUnavailableException | OperationTimedoutException e) { + logger.error("Failed PrepareExternalProvisioningCommand due to : {}", e.getMessage(), e); + throw cre; + } + if (answer == null) { + logger.error("Invalid answer received for PrepareExternalProvisioningCommand"); + throw cre; + } + if (!(answer instanceof PrepareExternalProvisioningAnswer)) { + logger.error("Unexpected answer received for PrepareExternalProvisioningCommand: [result: {}, details: {}]", + answer.getResult(), answer.getDetails()); + throw cre; + } + PrepareExternalProvisioningAnswer prepareAnswer = (PrepareExternalProvisioningAnswer)answer; + if (!prepareAnswer.getResult()) { + logger.error("Unexpected answer received for PrepareExternalProvisioningCommand: [result: {}, details: {}]", + answer.getResult(), answer.getDetails()); + throw cre; + } + updateExternalVmFromPrepareAnswer(virtualMachineTO, prepareAnswer.getVirtualMachineTO()); + } + @Override public void orchestrateStart(final String vmUuid, final Map params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { @@ -1135,6 +1329,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac VMInstanceVO vm = _vmDao.findByUuid(vmUuid); + final boolean firstStart = vm.getUpdated() == 0; + final VirtualMachineGuru vmGuru = getVmGuru(vm); final Account owner = _entityMgr.findById(Account.class, vm.getAccountId()); @@ -1224,10 +1420,17 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac planChangedByVolume = true; } } + DataStoreProvider storeProvider = dataStoreProviderManager.getDataStoreProvider(pool.getStorageProviderName()); + if (storeProvider != null) { + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + if (storeDriver instanceof PrimaryDataStoreDriver) { + ((PrimaryDataStoreDriver)storeDriver).detachVolumeFromAllStorageNodes(vol); + } + } } } - final VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, owner, params); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, owner, params); logBootModeParameters(params); DeployDestination dest = null; try { @@ -1270,8 +1473,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac try { resetVmNicsDeviceId(vm.getId()); + + processPrepareExternalProvisioning(firstStart, dest.getHost(), vmProfile, dest.getDataCenter()); + _networkMgr.prepare(vmProfile, dest, ctx); - if (vm.getHypervisorType() != HypervisorType.BareMetal) { + if (vm.getHypervisorType() != HypervisorType.BareMetal && vm.getHypervisorType() != HypervisorType.External) { checkAndAttemptMigrateVmAcrossCluster(vm, clusterId, dest.getStorageForDisks()); volumeMgr.prepare(vmProfile, dest); } @@ -1290,13 +1496,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac handlePath(vmTO.getDisks(), vm.getHypervisorType()); setVmNetworkDetails(vm, vmTO); - Commands cmds = new Commands(Command.OnError.Stop); final Map sshAccessDetails = _networkMgr.getSystemVMAccessDetails(vm); final Map ipAddressDetails = new HashMap<>(sshAccessDetails); ipAddressDetails.remove(NetworkElementCommand.ROUTER_NAME); StartCommand command = new StartCommand(vmTO, dest.getHost(), getExecuteInSequence(vm.getHypervisorType())); + updateStartCommandWithExternalDetails(dest.getHost(), vmTO, command); cmds.addCommand(command); vmGuru.finalizeDeployment(cmds, vmProfile, dest, ctx); @@ -1337,11 +1543,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final GPUDeviceTO gpuDevice = startAnswer.getVirtualMachine().getGpuDevice(); if (gpuDevice != null) { - _resourceMgr.updateGPUDetails(destHostId, gpuDevice.getGroupDetails()); + _resourceMgr.updateGPUDetailsForVmStart(destHostId, vm.getId(), gpuDevice); } - if (userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_VM) != null) { - userVmDetailsDao.removeDetail(vm.getId(), VmDetailConstants.DEPLOY_VM); + if (vmInstanceDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_VM) != null) { + vmInstanceDetailsDao.removeDetail(vm.getId(), VmDetailConstants.DEPLOY_VM); } startedVm = vm; @@ -1469,6 +1675,53 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } + protected void updateStartCommandWithExternalDetails(Host host, VirtualMachineTO vmTO, StartCommand command) { + if (!HypervisorType.External.equals(host.getHypervisorType())) { + return; + } + Map vmExternalDetails = vmTO.getExternalDetails(); + for (NicTO nic : vmTO.getNics()) { + if (!nic.isDefaultNic()) { + continue; + } + vmExternalDetails.put(VmDetailConstants.CLOUDSTACK_VLAN, networkService.getNicVlanValueForExternalVm(nic)); + } + Map> externalDetails = extensionsManager.getExternalAccessDetails(host, vmExternalDetails); + command.setExternalDetails(externalDetails); + } + + protected void updateStopCommandForExternalHypervisorType(final HypervisorType hypervisorType, + final VirtualMachineProfile vmProfile, final StopCommand stopCommand) { + if (!HypervisorType.External.equals(hypervisorType) || vmProfile.getHostId() == null) { + return; + } + Host host = _hostDao.findById(vmProfile.getHostId()); + if (host == null) { + return; + } + VirtualMachineTO vmTO = ObjectUtils.defaultIfNull(stopCommand.getVirtualMachine(), toVmTO(vmProfile)); + if (MapUtils.isEmpty(vmTO.getGuestOsDetails())) { + vmTO.setGuestOsDetails(null); + } + if (MapUtils.isEmpty(vmTO.getExtraConfig())) { + vmTO.setExtraConfig(null); + } + if (MapUtils.isEmpty(vmTO.getNetworkIdToNetworkNameMap())) { + vmTO.setNetworkIdToNetworkNameMap(null); + } + Map> externalDetails = extensionsManager.getExternalAccessDetails(host, vmTO.getExternalDetails()); + stopCommand.setVirtualMachine(vmTO); + stopCommand.setExternalDetails(externalDetails); + } + + protected void updateRebootCommandWithExternalDetails(Host host, VirtualMachineTO vmTO, RebootCommand rebootCmd) { + if (!HypervisorType.External.equals(host.getHypervisorType())) { + return; + } + Map> externalDetails = extensionsManager.getExternalAccessDetails(host, vmTO.getExternalDetails()); + rebootCmd.setExternalDetails(externalDetails); + } + public void setVmNetworkDetails(VMInstanceVO vm, VirtualMachineTO vmTO) { Map networkToNetworkNameMap = new HashMap<>(); if (VirtualMachine.Type.User.equals(vm.getType())) { @@ -1523,16 +1776,16 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final ClusterDetailsVO clusterDetailRam = _clusterDetailsDao.findDetail(clusterId, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO); final float parsedClusterCpuDetailCpu = Float.parseFloat(clusterDetailCpu.getValue()); final float parsedClusterDetailRam = Float.parseFloat(clusterDetailRam.getValue()); - UserVmDetailVO vmDetailCpu = userVmDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO); - UserVmDetailVO vmDetailRam = userVmDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO); + VMInstanceDetailVO vmDetailCpu = vmInstanceDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO); + VMInstanceDetailVO vmDetailRam = vmInstanceDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO); if ((vmDetailCpu == null && parsedClusterCpuDetailCpu > 1f) || (vmDetailCpu != null && Float.parseFloat(vmDetailCpu.getValue()) != parsedClusterCpuDetailCpu)) { - userVmDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO, clusterDetailCpu.getValue(), true); + vmInstanceDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO, clusterDetailCpu.getValue(), true); } if ((vmDetailRam == null && parsedClusterDetailRam > 1f) || (vmDetailRam != null && Float.parseFloat(vmDetailRam.getValue()) != parsedClusterDetailRam)) { - userVmDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO, clusterDetailRam.getValue(), true); + vmInstanceDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO, clusterDetailRam.getValue(), true); } vmProfile.setCpuOvercommitRatio(Float.parseFloat(clusterDetailCpu.getValue())); @@ -1856,7 +2109,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected boolean sendStop(final VirtualMachineGuru guru, final VirtualMachineProfile profile, final boolean force, final boolean checkBeforeCleanup) { final VirtualMachine vm = profile.getVirtualMachine(); Map vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId()); + StopCommand stpCmd = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), checkBeforeCleanup); + updateStopCommandForExternalHypervisorType(vm.getHypervisorType(), profile, stpCmd); if (MapUtils.isNotEmpty(vlanToPersistenceMap)) { stpCmd.setVlanToPersistenceMap(vlanToPersistenceMap); } @@ -1881,9 +2136,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } final GPUDeviceTO gpuDevice = stop.getGpuDevice(); - if (gpuDevice != null) { - _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails()); - } + _resourceMgr.updateGPUDetailsForVmStop(vm, gpuDevice); if (!answer.getResult()) { final String details = answer.getDetails(); logger.debug("Unable to stop VM due to {}", details); @@ -1987,7 +2240,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } try { - if (vm.getHypervisorType() != HypervisorType.BareMetal) { + if (vm.getHypervisorType() != HypervisorType.BareMetal && vm.getHypervisorType() != HypervisorType.External) { volumeMgr.release(profile); logger.debug("Successfully released storage resources for the VM {} in {} state", vm, state); } @@ -2184,6 +2437,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac Map vlanToPersistenceMap = getVlanToPersistenceMapForVM(vm.getId()); final StopCommand stop = new StopCommand(vm, getExecuteInSequence(vm.getHypervisorType()), false, cleanUpEvenIfUnableToStop); stop.setControlIp(getControlNicIpForVM(vm)); + updateStopCommandForExternalHypervisorType(vm.getHypervisorType(), profile, stop); if (MapUtils.isNotEmpty(vlanToPersistenceMap)) { stop.setVlanToPersistenceMap(vlanToPersistenceMap); } @@ -2211,9 +2465,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } vmGuru.finalizeStop(profile, answer); final GPUDeviceTO gpuDevice = stop.getGpuDevice(); - if (gpuDevice != null) { - _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails()); - } + _resourceMgr.updateGPUDetailsForVmStop(vm, gpuDevice); } else { throw new CloudRuntimeException("Invalid answer received in response to a StopCommand on " + vm.instanceName); } @@ -2233,6 +2485,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { logger.warn("Unable to actually stop {} but continue with release because it's a force stop", vm); vmGuru.finalizeStop(profile, answer); + if (HypervisorType.External.equals(profile.getHypervisorType())) { + try { + stateTransitTo(vm, VirtualMachine.Event.OperationSucceeded, null); + } catch (final NoTransitionException e) { + logger.warn("Unable to transition the state " + vm, e); + } + } + } } else { if (VirtualMachine.systemVMs.contains(vm.getType())) { @@ -2308,6 +2568,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac _reservationDao.setResourceId(Resource.ResourceType.user_vm, null); _reservationDao.setResourceId(Resource.ResourceType.cpu, null); _reservationDao.setResourceId(Resource.ResourceType.memory, null); + _reservationDao.setResourceId(Resource.ResourceType.gpu, null); } return _stateMachine.transitTo(vm, e, new Pair<>(vm.getHostId(), hostId), _vmDao); } @@ -2326,6 +2587,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac deleteVMSnapshots(vm, expunge); + gpuService.deallocateAllGpuDevicesForVm(vm.getId()); + Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) throws CloudRuntimeException { @@ -2336,6 +2599,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new CloudRuntimeException("Unable to destroy " + vm); } else { if (expunge) { + backupManager.checkAndRemoveBackupOfferingBeforeExpunge(vm); if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { logger.debug("Unable to expunge the vm because it is not in the correct state: {}", vm); throw new CloudRuntimeException("Unable to expunge " + vm); @@ -2843,11 +3107,29 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new CloudRuntimeException(details); } } catch (final OperationTimedoutException e) { - if (e.isActive()) { - logger.warn("Active migration command so scheduling a restart for {}", vm, e); - _haMgr.scheduleRestart(vm, true); + boolean success = false; + if (HypervisorType.KVM.equals(vm.getHypervisorType())) { + try { + final Answer answer = _agentMgr.send(vm.getHostId(), new CheckVirtualMachineCommand(vm.getInstanceName())); + if (answer != null && answer.getResult() && answer instanceof CheckVirtualMachineAnswer) { + final CheckVirtualMachineAnswer vmAnswer = (CheckVirtualMachineAnswer) answer; + if (VirtualMachine.PowerState.PowerOn.equals(vmAnswer.getState())) { + logger.info(String.format("Vm %s is found on destination host %s. Migration is successful", vm, vm.getHostId())); + success = true; + } + } + } catch (Exception ex) { + logger.error(String.format("Failed to get state of VM %s on destination host %s: %s", vm, vm.getHostId(), ex.getMessage())); + } + } + if (!success) { + if (e.isActive()) { + logger.warn("Active migration command so scheduling a restart for {}", vm, e); + _haMgr.scheduleRestart(vm, true); + + throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId); + } } - throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId); } try { @@ -2878,6 +3160,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac logger.info("Migration was unsuccessful. Cleaning up: {}", vm); _networkMgr.rollbackNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), dstHostId); + // deallocate GPU devices for the VM on the destination host + gpuService.deallocateGpuDevicesForVmOnHost(vm.getId(), dstHostId); _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(), "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " + dest.getDataCenter().getName() + " and pod " + @@ -2896,7 +3180,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { _networkMgr.commitNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), srcHostId); + // deallocate GPU devices for the VM on the src host after migration is complete + gpuService.deallocateGpuDevicesForVmOnHost(vm.getId(), srcHostId); _networkMgr.setHypervisorHostname(profile, dest, true); + recreateCheckpointsKvmOnVmAfterMigration(vm, dstHostId); updateVmPod(vm, dstHostId); } @@ -3275,7 +3562,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (defaultNic != null && VirtualMachine.Type.User.equals(vm.getType())) { UserVmVO userVm = _userVmDao.findById(vm.getId()); - Map details = userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + Map details = vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); userVm.setDetails(details); Network network = _networkModel.getNetwork(defaultNic.getNetworkId()); @@ -3346,6 +3633,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac _networkMgr.commitNicForMigration(vmSrc, profile); volumeMgr.release(vm.getId(), srcHostId); _networkMgr.setHypervisorHostname(profile, destination, true); + endSnapshotChainForVolumes(volumeToPoolMap, vm.getHypervisorType()); } work.setStep(Step.Done); @@ -3353,6 +3641,68 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } + protected void endSnapshotChainForVolumes(Map volumeToPoolMap, HypervisorType hypervisorType) { + Set volumes = volumeToPoolMap.keySet(); + volumes.forEach(volume -> { + Volume volumeOnDestination = _volsDao.findByPoolIdName(volumeToPoolMap.get(volume).getId(), volume.getName()); + snapshotManager.endSnapshotChainForVolume(volumeOnDestination.getId(), hypervisorType); + }); + } + + protected void recreateCheckpointsKvmOnVmAfterMigration(VMInstanceVO vm, long hostId) { + if (!HypervisorType.KVM.equals(vm.getHypervisorType())) { + logger.debug("Will not recreate checkpoint on VM as it is not running on KVM, thus it is not needed."); + return; + } + + List volumes = getVmVolumesWithCheckpointsToRecreate(vm); + + if (volumes.isEmpty()) { + logger.debug("Will not recreate checkpoints on VM as its volumes do not have any checkpoints associated with them."); + return; + } + + RecreateCheckpointsCommand recreateCheckpointsCommand = new RecreateCheckpointsCommand(volumes, vm.getInstanceName()); + Answer answer = null; + try { + logger.debug(String.format("Recreating the volume checkpoints with URLs [%s] of volumes [%s] on %s as part of the migration process.", volumes.stream().map(VolumeObjectTO::getCheckpointPaths).collect(Collectors.toList()), volumes, vm)); + answer = _agentMgr.send(hostId, recreateCheckpointsCommand); + } catch (AgentUnavailableException | OperationTimedoutException e) { + logger.error(String.format("Exception while sending command to host [%s] to recreate checkpoints with URLs [%s] of volumes [%s] on %s due to: [%s].", hostId, volumes.stream().map(VolumeObjectTO::getCheckpointPaths).collect(Collectors.toList()), volumes, vm, e.getMessage()), e); + throw new CloudRuntimeException(e); + } finally { + if (answer != null && answer.getResult()) { + logger.debug(String.format("Successfully recreated checkpoints on VM [%s].", vm)); + return; + } + + logger.debug(String.format("Migration on VM [%s] was successful; however, we weren't able to recreate the checkpoints on it. Marking the snapshot chain as ended." + + " Next snapshot will create a new snapshot chain.", vm)); + + volumes.forEach(volumeObjectTO -> snapshotManager.endSnapshotChainForVolume(volumeObjectTO.getId(), HypervisorType.KVM)); + } + } + + + protected List getVmVolumesWithCheckpointsToRecreate(VMInstanceVO vm) { + List vmVolumes = _volsDao.findByInstance(vm.getId()); + List volumes = new ArrayList<>(); + + for (VolumeVO volume : vmVolumes) { + Pair, Set> volumeCheckpointPathsAndImageStoreUrls = volumeMgr.getVolumeCheckpointPathsAndImageStoreUrls(volume.getId(), HypervisorType.KVM); + if (volumeCheckpointPathsAndImageStoreUrls.first().isEmpty()) { + continue; + } + VolumeObjectTO volumeTo = new VolumeObjectTO(); + volumeTo.setCheckpointPaths(volumeCheckpointPathsAndImageStoreUrls.first()); + volumeTo.setCheckpointImageStoreUrls(volumeCheckpointPathsAndImageStoreUrls.second()); + volumeTo.setPath(volume.getPath()); + volumes.add(volumeTo); + } + return volumes; + } + + @Override public VirtualMachineTO toVmTO(final VirtualMachineProfile profile) { final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(profile.getVirtualMachine().getHypervisorType()); @@ -3618,6 +3968,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac VirtualMachineTO vmTo = getVmTO(vm.getId()); checkAndSetEnterSetupMode(vmTo, params); rebootCmd.setVirtualMachine(vmTo); + updateRebootCommandWithExternalDetails(host, vmTo, rebootCmd); cmds.addCommand(rebootCmd); _agentMgr.send(host.getId(), cmds); @@ -3629,6 +3980,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac affectedVms.add(vm.getId()); _securityGroupManager.scheduleRulesetUpdateToHosts(affectedVms, true, null); } + if (vmTo.getGpuDevice() != null) { + _resourceMgr.updateGPUDetailsForVmStart(host.getId(), vm.getId(), vmTo.getGpuDevice()); + } return; } @@ -4730,29 +5084,29 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } private void removeCustomOfferingDetails(long vmId) { - Map details = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map details = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); details.remove(UsageEventVO.DynamicParameters.cpuNumber.name()); details.remove(UsageEventVO.DynamicParameters.cpuSpeed.name()); details.remove(UsageEventVO.DynamicParameters.memory.name()); - List detailList = new ArrayList<>(); + List detailList = new ArrayList<>(); for(Map.Entry entry: details.entrySet()) { - UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true); + VMInstanceDetailVO detailVO = new VMInstanceDetailVO(vmId, entry.getKey(), entry.getValue(), true); detailList.add(detailVO); } - userVmDetailsDao.saveDetails(detailList); + vmInstanceDetailsDao.saveDetails(detailList); } private void saveCustomOfferingDetails(long vmId, ServiceOffering serviceOffering) { - Map details = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map details = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); details.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); details.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); details.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); - List detailList = new ArrayList<>(); + List detailList = new ArrayList<>(); for (Map.Entry entry: details.entrySet()) { - UserVmDetailVO detailVO = new UserVmDetailVO(vmId, entry.getKey(), entry.getValue(), true); + VMInstanceDetailVO detailVO = new VMInstanceDetailVO(vmId, entry.getKey(), entry.getValue(), true); detailList.add(detailVO); } - userVmDetailsDao.saveDetails(detailList); + vmInstanceDetailsDao.saveDetails(detailList); } @Override @@ -5240,10 +5594,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = newVmWorkJobAndInfo.first(); VmWorkMigrateAway workInfo = new VmWorkMigrateAway(newVmWorkJobAndInfo.second(), srcHostId); - workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + setCmdInfoAndSubmitAsyncJob(workJob, workInfo, vmId); } - _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vmId); AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId()); @@ -6071,6 +6424,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public Map getDiskOfferingSuitabilityForVm(long vmId, List diskOfferingIds) { VMInstanceVO vm = _vmDao.findById(vmId); + if (vmInstanceDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_VM) != null) { + return new HashMap<>(); + } VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); Pair clusterAndHost = findClusterAndHostIdForVm(vm, false); Long clusterId = clusterAndHost.first(); @@ -6081,4 +6437,18 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } return result; } + + @Override + public void checkDeploymentPlan(VirtualMachine virtualMachine, VirtualMachineTemplate template, + ServiceOffering serviceOffering, Account systemAccount, DeploymentPlan plan) + throws InsufficientServerCapacityException { + final VirtualMachineProfileImpl vmProfile = + new VirtualMachineProfileImpl(virtualMachine, template, serviceOffering, systemAccount, null); + DeployDestination destination = + _dpMgr.planDeployment(vmProfile, plan, new DeploymentPlanner.ExcludeList(), null); + if (destination == null) { + throw new InsufficientServerCapacityException(String.format("Unable to create a deployment for %s", + vmProfile), DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile)); + } + } } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java index 4b344ac4299..7a1a39ec0f0 100644 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java @@ -77,19 +77,19 @@ public class VirtualMachinePowerStateSyncImpl implements VirtualMachinePowerStat processReport(hostId, translatedInfo, force); } - private void updateAndPublishVmPowerStates(long hostId, Map instancePowerStates, - Date updateTime) { + protected void updateAndPublishVmPowerStates(long hostId, Map instancePowerStates, + Date updateTime) { if (instancePowerStates.isEmpty()) { return; } Set vmIds = instancePowerStates.keySet(); - Map notUpdated = _instanceDao.updatePowerState(instancePowerStates, hostId, - updateTime); + Map notUpdated = + _instanceDao.updatePowerState(instancePowerStates, hostId, updateTime); if (notUpdated.size() > vmIds.size()) { return; } for (Long vmId : vmIds) { - if (!notUpdated.isEmpty() && !notUpdated.containsKey(vmId)) { + if (!notUpdated.containsKey(vmId)) { logger.debug("VM state report is updated. {}, {}, power state: {}", () -> hostCache.get(hostId), () -> vmCache.get(vmId), () -> instancePowerStates.get(vmId)); _messageBus.publish(null, VirtualMachineManager.Topics.VM_POWER_STATE, diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java index 19b0e773cd0..39ab83fab60 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineClusterVO.java @@ -114,6 +114,9 @@ public class EngineClusterVO implements EngineCluster, Identity { @Column(name = "engine_state", updatable = true, nullable = false, length = 32) protected State state = null; + @Column(name = "storage_access_groups") + String storageAccessGroups; + public EngineClusterVO() { clusterType = Cluster.ClusterType.CloudManaged; allocationState = Grouping.AllocationState.Enabled; @@ -176,6 +179,11 @@ public class EngineClusterVO implements EngineCluster, Identity { return managedState; } + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + public void setManagedState(ManagedState managedState) { this.managedState = managedState; } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostPodVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostPodVO.java index 95931d5b72d..cd3f6b857a2 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostPodVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostPodVO.java @@ -89,6 +89,9 @@ public class EngineHostPodVO implements EnginePod, Identity { @Temporal(value = TemporalType.TIMESTAMP) protected Date lastUpdated; + @Column(name = "storage_access_groups") + String storageAccessGroups; + /** * Note that state is intentionally missing the setter. Any updates to * the state machine needs to go through the DAO object because someone @@ -202,6 +205,11 @@ public class EngineHostPodVO implements EnginePod, Identity { return externalDhcp; } + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + public void setExternalDhcp(boolean use) { externalDhcp = use; } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java index 8ef2de3f74d..eec2b011b3e 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java @@ -405,6 +405,9 @@ public class EngineHostVO implements EngineHost, Identity { @Column(name = "engine_state", updatable = true, nullable = false, length = 32) protected State orchestrationState = null; + @Column(name = "storage_access_groups") + private String storageAccessGroups = null; + public EngineHostVO(String guid) { this.guid = guid; this.status = Status.Creating; @@ -807,4 +810,13 @@ public class EngineHostVO implements EngineHost, Identity { public PartitionType partitionType() { return PartitionType.Host; } + + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java index 6763a13aed6..8639f006383 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java @@ -18,6 +18,9 @@ */ package org.apache.cloudstack.engine.orchestration; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Volume; +import com.cloud.template.VirtualMachineTemplate; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -57,8 +60,9 @@ import com.cloud.utils.component.ComponentContext; import com.cloud.vm.NicProfile; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VmDiskInfo; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import static org.apache.cloudstack.api.ApiConstants.MAX_IOPS; @@ -83,7 +87,7 @@ public class CloudOrchestrator implements OrchestrationService { protected UserVmDao _userVmDao = null; @Inject - protected UserVmDetailsDao _userVmDetailsDao = null; + protected VMInstanceDetailsDao _vmInstanceDetailsDao = null; @Inject protected ServiceOfferingDao _serviceOfferingDao; @@ -158,8 +162,9 @@ public class CloudOrchestrator implements OrchestrationService { @Override public VirtualMachineEntity createVirtualMachine(String id, String owner, String templateId, String hostName, String displayName, String hypervisor, int cpu, - int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, - Long rootDiskSize, Map> extraDhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, Long dataDiskOfferingId, Long rootDiskOfferingId) throws InsufficientCapacityException { + int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, + Long rootDiskSize, Map> extraDhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, Long dataDiskOfferingId, Long rootDiskOfferingId, + List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, // vmEntityManager); @@ -184,7 +189,6 @@ public class CloudOrchestrator implements OrchestrationService { // Else, a disk offering is optional, and if present will be used to create the data disk DiskOfferingInfo rootDiskOfferingInfo = new DiskOfferingInfo(); - List dataDiskOfferings = new ArrayList(); ServiceOfferingVO computeOffering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); @@ -196,7 +200,7 @@ public class CloudOrchestrator implements OrchestrationService { rootDiskOfferingInfo.setSize(rootDiskSize); if (rootDiskOffering.isCustomizedIops() != null && rootDiskOffering.isCustomizedIops()) { - Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + Map userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); if (userVmDetails != null) { String minIops = userVmDetails.get(MIN_IOPS); @@ -207,6 +211,8 @@ public class CloudOrchestrator implements OrchestrationService { } } + List dataDiskOfferings = new ArrayList(); + List dataDiskDeviceIds = new ArrayList<>(); if (dataDiskOfferingId != null) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(dataDiskOfferingId); if (diskOffering == null) { @@ -228,7 +234,7 @@ public class CloudOrchestrator implements OrchestrationService { dataDiskOfferingInfo.setSize(size); if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { - Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + Map userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); if (userVmDetails != null) { String minIops = userVmDetails.get("minIopsDo"); @@ -240,6 +246,12 @@ public class CloudOrchestrator implements OrchestrationService { } dataDiskOfferings.add(dataDiskOfferingInfo); + dataDiskDeviceIds.add(null); + } + } else if (dataDiskInfoList != null){ + dataDiskOfferings.addAll(dataDiskInfoList); + for (VmDiskInfo dataDiskInfo : dataDiskInfoList) { + dataDiskDeviceIds.add(dataDiskInfo.getDeviceId()); } } @@ -254,9 +266,13 @@ public class CloudOrchestrator implements OrchestrationService { } } } - - _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(templateId)), computeOffering, rootDiskOfferingInfo, dataDiskOfferings, networkIpMap, plan, - hypervisorType, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap); + VirtualMachineTemplate template = null; + if (volume != null || snapshot != null) { + template = _templateDao.findByIdIncludingRemoved(new Long(templateId)); + } else + template = _templateDao.findById(new Long(templateId)); + _itMgr.allocate(vm.getInstanceName(), template, computeOffering, rootDiskOfferingInfo, dataDiskOfferings, dataDiskDeviceIds, + networkIpMap, plan, hypervisorType, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap, volume, snapshot); return vmEntity; } @@ -264,7 +280,7 @@ public class CloudOrchestrator implements OrchestrationService { @Override public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os, int cpu, int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, - Map> extraDhcpOptionMap, Long diskOfferingId) + Map> extraDhcpOptionMap, Long diskOfferingId, List dataDiskInfoList, Volume volume, Snapshot snapshot) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); @@ -300,7 +316,7 @@ public class CloudOrchestrator implements OrchestrationService { rootDiskOfferingInfo.setSize(size); if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { - Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + Map userVmDetails = _vmInstanceDetailsDao.listDetailsKeyPairs(vm.getId()); if (userVmDetails != null) { String minIops = userVmDetails.get("minIopsDo"); @@ -310,6 +326,14 @@ public class CloudOrchestrator implements OrchestrationService { rootDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); } } + List dataDiskOfferings = new ArrayList<>(); + List dataDiskDeviceIds = new ArrayList<>(); + if (dataDiskInfoList != null) { + dataDiskOfferings.addAll(dataDiskInfoList); + for (VmDiskInfo dataDiskInfo : dataDiskInfoList) { + dataDiskDeviceIds.add(dataDiskInfo.getDeviceId()); + } + } LinkedHashMap> networkIpMap = new LinkedHashMap>(); for (String uuid : networkNicMap.keySet()) { @@ -321,7 +345,8 @@ public class CloudOrchestrator implements OrchestrationService { HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor); - _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), computeOffering, rootDiskOfferingInfo, new ArrayList(), networkIpMap, plan, hypervisorType, extraDhcpOptionMap, null); + _itMgr.allocate(vm.getInstanceName(), _templateDao.findByIdIncludingRemoved(new Long(isoId)), computeOffering, rootDiskOfferingInfo, dataDiskOfferings, dataDiskDeviceIds, + networkIpMap, plan, hypervisorType, extraDhcpOptionMap, null, volume, snapshot); return vmEntity; } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java index c260f48dcf8..9609ba7751d 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtility.java @@ -208,9 +208,7 @@ public class DataMigrationUtility { List files = new LinkedList<>(); for (TemplateDataStoreVO template : templates) { VMTemplateVO templateVO = templateDao.findById(template.getTemplateId()); - if (template.getState() == ObjectInDataStoreStateMachine.State.Ready && templateVO != null && - (!templateVO.isPublicTemplate() || (templateVO.isPublicTemplate() && templateVO.getUrl() == null)) && - templateVO.getHypervisorType() != Hypervisor.HypervisorType.Simulator && templateVO.getParentTemplateId() == null) { + if (shouldMigrateTemplate(template, templateVO)) { files.add(templateFactory.getTemplate(template.getTemplateId(), srcDataStore)); } } @@ -231,6 +229,34 @@ public class DataMigrationUtility { return getAllReadyTemplates(srcDataStore, childTemplates, templates); } + /** + * Returns whether a template should be migrated. A template should be migrated if: + *
    + *
  1. its state is ready, and
  2. + *
  3. its hypervisor type is not simulator, and
  4. + *
  5. it is not a child template.
  6. + *
+ */ + protected boolean shouldMigrateTemplate(TemplateDataStoreVO template, VMTemplateVO templateVO) { + if (template.getState() != State.Ready) { + logger.debug("Template [{}] should not be migrated as it is not ready.", template); + return false; + } + + if (templateVO.getHypervisorType() == Hypervisor.HypervisorType.Simulator) { + logger.debug("Template [{}] should not be migrated as its hypervisor type is simulator.", template); + return false; + } + + if (templateVO.getParentTemplateId() != null) { + logger.debug("Template [{}] should not be migrated as it has a parent template.", template); + return false; + } + + logger.debug("Template [{}] should be migrated.", template); + return true; + } + /** Returns parent snapshots and snapshots that do not have any children; snapshotChains comprises of the snapshot chain info * for each parent snapshot and the cumulative size of the chain - this is done to ensure that all the snapshots in a chain * are migrated to the same datastore diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index b0081c6e685..146643bae22 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -16,11 +16,14 @@ // under the License. package org.apache.cloudstack.engine.orchestration; +import static com.cloud.configuration.ConfigurationManager.MESSAGE_DELETE_VLAN_IP_RANGE_EVENT; + import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -38,12 +41,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.dc.ASNumberVO; -import com.cloud.bgp.BGPService; -import com.cloud.dc.VlanDetailsVO; -import com.cloud.dc.dao.ASNumberDao; -import com.cloud.dc.dao.VlanDetailsDao; -import com.cloud.network.dao.NsxProviderDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -65,6 +62,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -86,8 +84,10 @@ import com.cloud.agent.api.to.deployasis.OVFNetworkTO; import com.cloud.alert.AlertManager; import com.cloud.api.query.dao.DomainRouterJoinDao; import com.cloud.api.query.vo.DomainRouterJoinVO; +import com.cloud.bgp.BGPService; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.ASNumberVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -95,12 +95,15 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVnetVO; import com.cloud.dc.PodVlanMapVO; import com.cloud.dc.Vlan; +import com.cloud.dc.VlanDetailsVO; import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.ASNumberDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DataCenterVnetDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; +import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; @@ -151,6 +154,8 @@ import com.cloud.network.dao.AccountGuestVlanMapVO; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao; +import com.cloud.network.dao.NetrisProviderDao; import com.cloud.network.dao.NetworkAccountDao; import com.cloud.network.dao.NetworkAccountVO; import com.cloud.network.dao.NetworkDao; @@ -161,6 +166,7 @@ import com.cloud.network.dao.NetworkDomainVO; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkServiceMapVO; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; @@ -248,8 +254,8 @@ import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; @@ -261,9 +267,6 @@ import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; import com.googlecode.ipv6.IPv6Address; -import org.jetbrains.annotations.NotNull; - -import static com.cloud.configuration.ConfigurationManager.MESSAGE_DELETE_VLAN_IP_RANGE_EVENT; /** * NetworkManagerImpl implements NetworkManager. @@ -355,9 +358,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Inject private NsxProviderDao nsxProviderDao; @Inject + private NetrisProviderDao netrisProviderDao; + @Inject private ASNumberDao asNumberDao; @Inject private BGPService bgpService; + @Inject + private Ipv6GuestPrefixSubnetNetworkMapDao ipv6GuestPrefixSubnetNetworkMapDao; @Override public List getNetworkGurus() { @@ -558,27 +565,27 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true, Availability.Optional, null, new HashMap>(), true, Network.GuestType.Shared, false, null, true, null, true, - false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); + false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false); } //#2 - SG enabled network offering if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); + null, true, false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false); } //#3 - shared network offering with no SG service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, - null, true, false, false, false, null,null, null, true, null, null, false); + null, true, false, false, false, false, null, null, null, true, null, null, false); } if (_networkOfferingDao.findByUniqueName(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultTungstenSharedSGEnabledNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, true, false, null, null,null, true, null, null, false); + null, true, false, null, false, null, true, false, true, false, false, null, null, null, true, null, null, false); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } @@ -588,14 +595,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService, "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null, defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, - true, false, false, false, null, null,null, true, null, null, false); + true, false, false, false, false, null, null, null, true, null, null, false); } //#5 - default vpc offering with LB service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null, - defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false); + defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, true, true, false, false, false, null, null, null, true, null, null, false); } //#6 - default vpc offering with no LB service @@ -604,14 +611,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra defaultVPCOffProviders.remove(Service.Lb); offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional, - null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false); + null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false, null, null, null, true, null, null, false); } //#7 - isolated offering with source nat disabled if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service", TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, - true, null, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); + true, null, true, false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false); } //#8 - network offering with internal lb service @@ -633,7 +640,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders, - true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, true, null, null, false); + true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, false, null, null, null, true, null, null, false); offering.setInternalLb(true); offering.setPublicLb(false); _networkOfferingDao.update(offering.getId(), offering); @@ -664,7 +671,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null, - netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, null, null, null, true, null, null, false); + netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, false, null, null, null, true, null, null, false); offering.setDedicatedLB(false); _networkOfferingDao.update(offering.getId(), offering); } @@ -821,6 +828,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (domainId != null && aclType == ACLType.Domain) { _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null || subdomainAccess); } + String ipv6Cidr = network.getIp6Cidr(); + String ipv6Gateway = network.getIp6Gateway(); + if (StringUtils.isNoneBlank(ipv6Cidr, ipv6Gateway)) { + ipv6Service.assignIpv6SubnetToNetwork(ipv6Cidr, networkPersisted.getId()); + } } }); guru.setup(network, relatedFile); @@ -1055,7 +1067,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return Transaction.execute(new TransactionCallback() { @Override public NicVO doInTransaction(TransactionStatus status) { - NicVO vo = _nicDao.findByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId); + NicVO vo = _nicDao.findNonPlaceHolderByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId); if (vo == null) { applyProfileToNic(nic, profile, deviceId); vo = _nicDao.persist(nic); @@ -1082,10 +1094,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return null; } - if (isNicAllocatedForNsxPublicNetworkOnVR(network, profile, vm)) { + if (isNicAllocatedForProviderPublicNetworkOnVR(network, profile, vm, Provider.Nsx)) { String guruName = "NsxPublicNetworkGuru"; NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName); nsxGuru.allocate(network, profile, vm); + } else if (isNicAllocatedForProviderPublicNetworkOnVR(network, profile, vm, Provider.Netris)) { + String guruName = "NetrisPublicNetworkGuru"; + NetworkGuru netrisGuru = AdapterBase.getAdapterByName(networkGurus, guruName); + netrisGuru.allocate(network, profile, vm); } if (isDefaultNic != null) { @@ -1148,7 +1164,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return new Pair(vmNic, Integer.valueOf(deviceId)); } - private boolean isNicAllocatedForNsxPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm) { + private boolean isNicAllocatedForProviderPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm, Provider provider) { if (ObjectUtils.anyNull(network, requested, vm)) { return false; } @@ -1158,7 +1174,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return false; } long dataCenterId = vm.getVirtualMachine().getDataCenterId(); - if (nsxProviderDao.findByZoneId(dataCenterId) == null) { + if (Provider.Nsx == provider && nsxProviderDao.findByZoneId(dataCenterId) == null) { + return false; + } else if (Provider.Netris == provider && netrisProviderDao.findByZoneId(dataCenterId) == null) { return false; } @@ -1168,14 +1186,16 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (CollectionUtils.isEmpty(ips)) { return false; } + ips = ips.stream().filter(x -> !x.getAddress().addr().equals(requested.getIPv4Address())).collect(Collectors.toList()); IPAddressVO ip = ips.get(0); - VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), ApiConstants.NSX_DETAIL_KEY); + String detailKey = Provider.Nsx == provider ? ApiConstants.NSX_DETAIL_KEY : ApiConstants.NETRIS_DETAIL_KEY; + VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), detailKey); if (vlanDetail == null) { return false; } - boolean isForNsx = vlanDetail.getValue().equalsIgnoreCase("true"); - return isForNsx && !ip.isForSystemVms(); + boolean isForProvider = vlanDetail.getValue().equalsIgnoreCase("true"); + return isForProvider && !ip.isForSystemVms(); } private void setMtuDetailsInVRNic(final Pair networks, Network network, NicVO vo) { @@ -1291,6 +1311,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra IPAddressVO lockedIpVO = _ipAddressDao.acquireInLockTable(ipVO.getId()); validateLockedRequestedIp(ipVO, lockedIpVO); lockedIpVO.setState(IPAddressVO.State.Allocated); + lockedIpVO.setAllocatedTime(new Date()); _ipAddressDao.update(lockedIpVO.getId(), lockedIpVO); } finally { _ipAddressDao.releaseFromLockTable(ipVO.getId()); @@ -1528,8 +1549,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (isNetworkImplemented(network)) { logger.debug("Network {} is already implemented", network); implemented.set(guru, network); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_UPDATE, network.getAccountId(), network.getDataCenterId(), network.getId(), - network.getName(), network.getNetworkOfferingId(), null, network.getState().name(), Network.class.getName(), network.getUuid(), true); return implemented; } @@ -1585,9 +1604,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra network.setRestartRequired(false); _networksDao.update(network.getId(), network); + UsageEventUtils.publishNetworkUpdate(network); implemented.set(guru, network); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_CREATE, network.getAccountId(), network.getDataCenterId(), network.getId(), - network.getName(), network.getNetworkOfferingId(), null, null, null, network.getState().name(), network.getUuid()); return implemented; } catch (final NoTransitionException e) { logger.error(e.getMessage()); @@ -1696,6 +1714,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } } + reconfigureAndApplyStaticRouteForVpcVpn(network); } finally { for (final NetworkElement element : networkElements) { if (element instanceof AggregatedCommandExecutor && providersToImplement.contains(element.getProvider())) { @@ -1705,6 +1724,17 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } + private void reconfigureAndApplyStaticRouteForVpcVpn(Network network) { + if (network.getVpcId() != null) { + _vpcMgr.reconfigStaticNatForVpcVr(network.getVpcId()); + try { + _vpcMgr.applyStaticRouteForVpcVpnIfNeeded(network.getVpcId(), true); + } catch (ResourceUnavailableException e) { + logger.error("Unable to apply static routes for vpc " + network.getVpcId() + " due to " + e.getMessage()); + } + } + } + private void implementNetworkElements(final DeployDestination dest, final ReservationContext context, final Network network, final NetworkOffering offering, final List providersToImplement) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { for (NetworkElement element : networkElements) { @@ -1941,6 +1971,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra ip.setOneToOneNat(false); ip.setAssociatedWithVmId(null); ip.setVmIp(null); + ip.setForRouter(false); _ipAddressDao.update(ip.getId(), ip); } } @@ -3087,6 +3118,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (updateResourceCount) { _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled); } + UsageEventUtils.publishNetworkCreation(network); return network; } @@ -3168,13 +3200,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } logger.debug("Lock is acquired for network {} as a part of network shutdown", network); - if (network.getState() == Network.State.Allocated) { - logger.debug("Network is already shutdown: {}", network); + final Network.State initialState = network.getState(); + if (initialState == Network.State.Allocated) { + logger.debug(String.format("Network [%s] is in Allocated state, no need to shutdown.", network)); return true; } - if (network.getState() != Network.State.Implemented && network.getState() != Network.State.Shutdown) { - logger.debug("Network is not implemented: {}", network); + if (initialState != Network.State.Implemented && initialState != Network.State.Shutdown) { + logger.debug("Network is not implemented: " + network); return false; } @@ -3218,6 +3251,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } _networksDao.update(networkFinal.getId(), networkFinal); _networksDao.clearCheckForGc(networkId); + if (initialState == Network.State.Implemented) { + UsageEventUtils.publishNetworkUpdate(networkFinal); + } result = true; } else { try { @@ -3293,6 +3329,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } } + reconfigureAndApplyStaticRouteForVpcVpn(network); return success; } @@ -3469,8 +3506,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final Pair, Long> networkMsg = new Pair, Long>(Network.class, networkFinal.getId()); _messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, networkMsg); } - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_DELETE, network.getAccountId(), network.getDataCenterId(), network.getId(), - network.getName(), network.getNetworkOfferingId(), null, null, null, Network.class.getName(), network.getUuid()); + UsageEventUtils.publishNetworkDeletion(network); return true; } catch (final CloudRuntimeException e) { logger.error("Failed to delete network", e); @@ -4875,6 +4911,6 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return new ConfigKey[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, DeniedRoutes, GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RollingRestartEnabled, - TUNGSTEN_ENABLED, NSX_ENABLED }; + TUNGSTEN_ENABLED, NSX_ENABLED, NETRIS_ENABLED }; } } diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index db0119febde..7af9b6b8492 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -38,10 +38,12 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.deploy.DeploymentClusterPlanner; import com.cloud.exception.ResourceAllocationException; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.AccountManager; import org.apache.cloudstack.api.ApiCommandResourceType; import org.apache.cloudstack.api.ApiConstants.IoDriverPolicy; @@ -55,6 +57,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; @@ -73,6 +77,7 @@ import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; @@ -81,8 +86,10 @@ import org.apache.cloudstack.secret.PassphraseVO; import org.apache.cloudstack.secret.dao.PassphraseDao; import org.apache.cloudstack.snapshot.SnapshotHelper; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; @@ -161,7 +168,7 @@ import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.DiskProfile; import com.cloud.vm.SecondaryStorageVmVO; import com.cloud.vm.UserVmCloneSettingVO; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -176,7 +183,8 @@ import com.cloud.vm.VmWorkTakeVolumeSnapshot; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; +import com.cloud.vm.dao.VMInstanceDao; public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrationService, Configurable { @@ -198,6 +206,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @Inject protected PrimaryDataStoreDao _storagePoolDao = null; @Inject + protected ImageStoreDao imageStoreDao; + @Inject protected TemplateDataStoreDao _vmTemplateStoreDao = null; @Inject protected VolumeDao _volumeDao; @@ -246,7 +256,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @Inject TemplateService templateService; @Inject - UserVmDetailsDao userVmDetailsDao; + VMInstanceDetailsDao vmInstanceDetailsDao; @Inject private SecondaryStorageVmDao secondaryStorageVmDao; @Inject @@ -257,10 +267,19 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati StoragePoolHostDao storagePoolHostDao; @Inject DiskOfferingDao diskOfferingDao; + @Inject + ConfigDepot configDepot; + @Inject + ConfigurationDao configurationDao; + @Inject + VMInstanceDao vmInstanceDao; @Inject protected SnapshotHelper snapshotHelper; + @Inject + private DataStoreProviderManager dataStoreProviderMgr; + private final StateMachine2 _volStateMachine; protected List _storagePoolAllocators; @@ -561,21 +580,34 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } VolumeInfo vol = volFactory.getVolume(volume.getId()); + long zoneId = volume.getDataCenterId(); DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); - DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot); - SnapshotInfo snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, volume.getDataCenterId()); - - boolean kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole); + DataStoreRole dataStoreRole = snapshotHelper.getDataStoreRole(snapshot, zoneId); + SnapshotInfo snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, zoneId); + boolean kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole, zoneId); + logger.debug("Creating volume from snapshot, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage); + boolean storageSupportSnapshotToTemplateEnabled = snapshotHelper.isStorageSupportSnapshotToTemplate(snapInfo); // storageSupportSnapshotToTemplateEnabled is true only for StorPool now [TODO: Update to check storage supports snapshot to volume (DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT) - may impact other storages, or StorPool storage type only] try { - snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage); + if (!storageSupportSnapshotToTemplateEnabled) { + dataStoreRole = snapshotHelper.getDataStoreRole(snapshot); + snapInfo = snapshotFactory.getSnapshotWithRoleAndZone(snapshot.getId(), dataStoreRole, zoneId); + kvmSnapshotOnlyInPrimaryStorage = snapshotHelper.isKvmSnapshotOnlyInPrimaryStorage(snapshot, dataStoreRole); + logger.debug("Creating volume from snapshot, with dataStore role {} and on primary storage: {}", dataStoreRole, kvmSnapshotOnlyInPrimaryStorage); + snapInfo = snapshotHelper.backupSnapshotToSecondaryStorageIfNotExists(snapInfo, dataStoreRole, snapshot, kvmSnapshotOnlyInPrimaryStorage); + } } catch (CloudRuntimeException e) { snapshotHelper.expungeTemporarySnapshot(kvmSnapshotOnlyInPrimaryStorage, snapInfo); throw e; } + boolean kvmIncrementalSnapshot = SnapshotManager.kvmIncrementalSnapshot.valueIn(_hostDao.findClusterIdByVolumeInfo(snapInfo.getBaseVolume())); + if (kvmIncrementalSnapshot && DataStoreRole.Image.equals(dataStoreRole)) { + snapInfo = snapshotHelper.convertSnapshotIfNeeded(snapInfo); + } + // don't try to perform a sync if the DataStoreRole of the snapshot is equal to DataStoreRole.Primary - if (!DataStoreRole.Primary.equals(dataStoreRole) || kvmSnapshotOnlyInPrimaryStorage) { + if (!DataStoreRole.Primary.equals(dataStoreRole) || (kvmSnapshotOnlyInPrimaryStorage && !storageSupportSnapshotToTemplateEnabled)) { try { // sync snapshot to region store if necessary DataStore snapStore = snapInfo.getDataStore(); @@ -882,10 +914,20 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, - Account owner, long deviceId, String configurationId) { + Account owner, long deviceId, String configurationId, Volume volume, Snapshot snapshot) { assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template."; - Long size = _tmpltMgr.getTemplateSize(template, vm.getDataCenterId()); + if (volume != null) { + volume = attachExistingVolumeToVm(vm, deviceId, volume, type); + provideVmInfoToTheStorageVolume(vm, volume); + return toDiskProfile(volume, offering); + } + Long size; + if (snapshot != null) { + size = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId()).getSize(); + } else { + size = _tmpltMgr.getTemplateSize(template, vm.getDataCenterId()); + } if (rootDisksize != null) { if (template.isDeployAsIs()) { // Volume size specified from template deploy-as-is @@ -933,9 +975,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati // Create event and update resource count for volumes if vm is a user vm if (vm.getType() == VirtualMachine.Type.User) { - Long offeringId = null; - if (!offering.isComputeOnly()) { offeringId = offering.getId(); } @@ -945,9 +985,45 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati _resourceLimitMgr.incrementVolumeResourceCount(vm.getAccountId(), vol.isDisplayVolume(), vol.getSize(), offering); } + if (snapshot != null) { + UserVmVO userVmVO = _userVmDao.findById(vm.getId()); + try { + VolumeInfo volumeInfo = createVolumeFromSnapshot(vol, snapshot, userVmVO); + return toDiskProfile(volumeInfo, offering); + } catch (StorageUnavailableException ex) { + throw new CloudRuntimeException("Could not create volume from a snapshot", ex); + } + } return toDiskProfile(vol, offering); } + private void provideVmInfoToTheStorageVolume(VirtualMachine vm, Volume volume) { + + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); + if (pool != null) { + DataStoreProvider storeProvider = dataStoreProviderMgr + .getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + if (storeDriver != null && storeDriver instanceof PrimaryDataStoreDriver && ((PrimaryDataStoreDriver) storeDriver).isVmInfoNeeded()) { + ((PrimaryDataStoreDriver) storeDriver).provideVmInfo(vm.getId(), volume.getId()); + } + } + } + + private Volume attachExistingVolumeToVm(VirtualMachine vm, long deviceId, Volume volume, Type type) { + VolumeVO volumeVO = _volumeDao.findById(volume.getId()); + if (volumeVO == null) { + throw new CloudRuntimeException(String.format("Could not find the volume %s in the DB", volume)); + } + volumeVO.setDeviceId(deviceId); + volumeVO.setVolumeType(type); + if (vm != null) { + volumeVO.setInstanceId(vm.getId()); + } + _volumeDao.update(volumeVO.getId(), volumeVO); + return volumeVO; + } + @Override public void saveVolumeDetails(Long diskOfferingId, Long volumeId) { List volumeDetailsVO = new ArrayList<>(); @@ -977,7 +1053,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating ROOT volume", create = true) @Override public List allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, - Account owner) { + Account owner, Volume volume, Snapshot snapshot) { String templateToString = getReflectOnlySelectedFields(template); int volumesNumber = 1; @@ -990,7 +1066,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati logger.info("Could not find a running SSVM in datacenter [{}] for deploying VM as is. Not deploying VM [{}] as is.", vm.getDataCenterId(), vm); } else { - UserVmDetailVO configurationDetail = userVmDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_AS_IS_CONFIGURATION); + VMInstanceDetailVO configurationDetail = vmInstanceDetailsDao.findDetail(vm.getId(), VmDetailConstants.DEPLOY_AS_IS_CONFIGURATION); if (configurationDetail != null) { configurationId = configurationDetail.getValue(); } @@ -1024,7 +1100,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } logger.info("Adding disk object [{}] to VM [{}]", volumeName, vm); DiskProfile diskProfile = allocateTemplatedVolume(type, volumeName, offering, volumeSize, minIops, maxIops, - template, vm, owner, deviceId, configurationId); + template, vm, owner, deviceId, configurationId, volume, snapshot); profiles.add(diskProfile); } @@ -1059,13 +1135,13 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati String diskControllerSubType = disksAsIs.get(0).getDiskControllerSubType(); if (StringUtils.isNotBlank(diskControllerSubType)) { long vmId = vm.getId(); - UserVmDetailVO detail = userVmDetailsDao.findDetail(vmId, VmDetailConstants.ROOT_DISK_CONTROLLER); + VMInstanceDetailVO detail = vmInstanceDetailsDao.findDetail(vmId, VmDetailConstants.ROOT_DISK_CONTROLLER); if (detail != null) { detail.setValue(diskControllerSubType); - userVmDetailsDao.update(detail.getId(), detail); + vmInstanceDetailsDao.update(detail.getId(), detail); } else { - detail = new UserVmDetailVO(vmId, VmDetailConstants.ROOT_DISK_CONTROLLER, diskControllerSubType, false); - userVmDetailsDao.persist(detail); + detail = new VMInstanceDetailVO(vmId, VmDetailConstants.ROOT_DISK_CONTROLLER, diskControllerSubType, false); + vmInstanceDetailsDao.persist(detail); } } } @@ -1554,7 +1630,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati private void setIoDriverPolicy(Map details, StoragePoolVO storagePool, VolumeVO volume) { if (volume.getInstanceId() != null) { - UserVmDetailVO ioDriverPolicy = userVmDetailsDao.findDetail(volume.getInstanceId(), + VMInstanceDetailVO ioDriverPolicy = vmInstanceDetailsDao.findDetail(volume.getInstanceId(), VmDetailConstants.IO_POLICY); if (ioDriverPolicy != null) { if (IoDriverPolicy.STORAGE_SPECIFIC.toString().equals(ioDriverPolicy.getValue())) { @@ -1868,14 +1944,18 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati if (newSize != vol.getSize()) { DiskOfferingVO diskOffering = diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()); - if (newSize > vol.getSize()) { - _resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()), - vol.isDisplay(), newSize - vol.getSize(), diskOffering); - _resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), - newSize - vol.getSize(), diskOffering); - } else { - _resourceLimitMgr.decrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), - vol.getSize() - newSize, diskOffering); + VMInstanceVO vm = vol.getInstanceId() != null ? vmInstanceDao.findById(vol.getInstanceId()) : null; + if (vm == null || vm.getType() == VirtualMachine.Type.User) { + // Update resource count for user vm volumes when volume is attached + if (newSize > vol.getSize()) { + _resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()), + vol.isDisplay(), newSize - vol.getSize(), diskOffering); + _resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), + newSize - vol.getSize(), diskOffering); + } else { + _resourceLimitMgr.decrementVolumePrimaryStorageResourceCount(vol.getAccountId(), vol.isDisplay(), + vol.getSize() - newSize, diskOffering); + } } vol.setSize(newSize); _volsDao.persist(vol); @@ -1974,10 +2054,29 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati _vmCloneSettingDao.persist(vmCloneSettingVO); } } - } } + @Override + public Pair, Set> getVolumeCheckpointPathsAndImageStoreUrls(long volumeId, HypervisorType hypervisorType) { + List checkpointPaths = new ArrayList<>(); + Set imageStoreIds = new HashSet<>(); + Set imageStoreUrls = new HashSet<>(); + if (HypervisorType.KVM.equals(hypervisorType)) { + List snapshotDataStoreVos = _snapshotDataStoreDao.listReadyByVolumeIdAndCheckpointPathNotNull(volumeId); + snapshotDataStoreVos.forEach(snapshotDataStoreVO -> { + checkpointPaths.add(snapshotDataStoreVO.getKvmCheckpointPath()); + if (DataStoreRole.Image.equals(snapshotDataStoreVO.getRole())) { + imageStoreIds.add(snapshotDataStoreVO.getDataStoreId()); + } + }); + imageStoreUrls = imageStoreIds.stream().map(id -> imageStoreDao.findById(id).getUrl()).collect(Collectors.toSet()); + logger.debug(String.format("Found [%s] snapshots [%s] that have checkpoints for volume with id [%s].", snapshotDataStoreVos.size(), snapshotDataStoreVos, volumeId)); + } + + return new Pair<>(checkpointPaths, imageStoreUrls); + } + private void handleCheckAndRepairVolume(Volume vol, Long hostId) { Host host = _hostDao.findById(hostId); try { @@ -2018,7 +2117,9 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {RecreatableSystemVmEnabled, MaxVolumeSize, StorageHAMigrationEnabled, StorageMigrationEnabled, CustomDiskOfferingMaxSize, CustomDiskOfferingMinSize, VolumeUrlCheck}; + return new ConfigKey[] { + RecreatableSystemVmEnabled, MaxVolumeSize, StorageHAMigrationEnabled, StorageMigrationEnabled, + CustomDiskOfferingMaxSize, CustomDiskOfferingMinSize, VolumeUrlCheck, VolumeAllocationAlgorithm}; } @Override @@ -2031,6 +2132,18 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati return true; } + @Override + public boolean start() { + if (configDepot.isNewConfig(VolumeAllocationAlgorithm)) { + String vmAllocationAlgo = DeploymentClusterPlanner.VmAllocationAlgorithm.value(); + if (com.cloud.utils.StringUtils.isNotEmpty(vmAllocationAlgo) && !VolumeAllocationAlgorithm.defaultValue().equalsIgnoreCase(vmAllocationAlgo)) { + logger.debug("Updating value for configuration: {} to {}", VolumeAllocationAlgorithm.key(), vmAllocationAlgo); + configurationDao.update(VolumeAllocationAlgorithm.key(), vmAllocationAlgo); + } + } + return true; + } + private void cleanupVolumeDuringAttachFailure(Long volumeId, Long vmId) { VolumeVO volume = _volsDao.findById(volumeId); if (volume == null) { diff --git a/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java index 52b7ed77533..fb42f247788 100644 --- a/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentManagerImplTest.java @@ -25,6 +25,7 @@ import com.cloud.exception.ConnectionException; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.Pair; import org.junit.Assert; import org.junit.Before; @@ -47,7 +48,7 @@ public class AgentManagerImplTest { host = new HostVO("some-Uuid"); host.setDataCenterId(1L); cmds = new StartupCommand[]{new StartupRoutingCommand()}; - attache = new ConnectedAgentAttache(null, 1L, "uuid", "kvm-attache", null, false); + attache = new ConnectedAgentAttache(null, 1L, "uuid", "kvm-attache", Hypervisor.HypervisorType.KVM, null, false); hostDao = Mockito.mock(HostDao.class); storagePoolMonitor = Mockito.mock(Listener.class); diff --git a/engine/orchestration/src/test/java/com/cloud/agent/manager/ClusteredAgentManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/agent/manager/ClusteredAgentManagerImplTest.java new file mode 100644 index 00000000000..5e4678f6222 --- /dev/null +++ b/engine/orchestration/src/test/java/com/cloud/agent/manager/ClusteredAgentManagerImplTest.java @@ -0,0 +1,150 @@ +// 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.manager; + +import com.cloud.configuration.ManagementServiceConfiguration; +import com.cloud.ha.HighAvailabilityManagerImpl; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.resource.ResourceManagerImpl; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ClusteredAgentManagerImplTest { + + private HostDao _hostDao; + @Mock + ManagementServiceConfiguration _mgmtServiceConf; + + @Before + public void setUp() throws Exception { + _hostDao = mock(HostDao.class); + } + + @Test + public void scanDirectAgentToLoadNoHostsTest() { + ClusteredAgentManagerImpl clusteredAgentManagerImpl = mock(ClusteredAgentManagerImpl.class); + clusteredAgentManagerImpl._hostDao = _hostDao; + clusteredAgentManagerImpl.scanDirectAgentToLoad(); + verify(clusteredAgentManagerImpl, never()).findAttache(anyLong()); + verify(clusteredAgentManagerImpl, never()).loadDirectlyConnectedHost(any(), anyBoolean()); + } + + @Test + public void scanDirectAgentToLoadHostWithoutAttacheTest() { + // Arrange + ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class); + HostVO hostVO = mock(HostVO.class); + clusteredAgentManagerImpl._hostDao = _hostDao; + clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf; + clusteredAgentManagerImpl._resourceMgr = mock(ResourceManagerImpl.class); + when(_mgmtServiceConf.getTimeout()).thenReturn(16000L); + when(hostVO.getId()).thenReturn(1L); + List hosts = new ArrayList<>(); + hosts.add(hostVO); + when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts); + AgentAttache agentAttache = mock(AgentAttache.class); + doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false); + clusteredAgentManagerImpl.scanDirectAgentToLoad(); + verify(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false); + } + + @Test + public void scanDirectAgentToLoadHostWithForwardAttacheTest() { + ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class); + HostVO hostVO = mock(HostVO.class); + clusteredAgentManagerImpl._hostDao = _hostDao; + clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf; + when(_mgmtServiceConf.getTimeout()).thenReturn(16000L); + when(hostVO.getId()).thenReturn(1L); + List hosts = new ArrayList<>(); + hosts.add(hostVO); + when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts); + AgentAttache agentAttache = mock(AgentAttache.class); + when(agentAttache.forForward()).thenReturn(Boolean.TRUE); + when(clusteredAgentManagerImpl.findAttache(1L)).thenReturn(agentAttache); + + clusteredAgentManagerImpl.scanDirectAgentToLoad(); + verify(clusteredAgentManagerImpl).removeAgent(agentAttache, Status.Disconnected); + } + + @Test + public void scanDirectAgentToLoadHostWithNonForwardAttacheTest() { + // Arrange + ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(new ClusteredAgentManagerImpl()); + HostVO hostVO = mock(HostVO.class); + clusteredAgentManagerImpl._hostDao = _hostDao; + clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf; + clusteredAgentManagerImpl._haMgr = mock(HighAvailabilityManagerImpl.class); + when(_mgmtServiceConf.getTimeout()).thenReturn(16000L); + when(hostVO.getId()).thenReturn(0L); + List hosts = new ArrayList<>(); + hosts.add(hostVO); + when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts); + + AgentAttache agentAttache = mock(AgentAttache.class); + when(agentAttache.forForward()).thenReturn(Boolean.FALSE); + when(clusteredAgentManagerImpl.findAttache(0L)).thenReturn(agentAttache); + doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).agentStatusTransitTo(hostVO, Status.Event.Ping, clusteredAgentManagerImpl._nodeId); + doReturn(Status.Up).when(clusteredAgentManagerImpl).investigate(agentAttache); + + clusteredAgentManagerImpl.scanDirectAgentToLoad(); + verify(clusteredAgentManagerImpl).investigate(agentAttache); + verify(clusteredAgentManagerImpl).agentStatusTransitTo(hostVO, Status.Event.Ping, clusteredAgentManagerImpl._nodeId); + } + + @Test + public void scanDirectAgentToLoadHostWithNonForwardAttacheAndDisconnectedTest() { + ClusteredAgentManagerImpl clusteredAgentManagerImpl = Mockito.spy(ClusteredAgentManagerImpl.class); + HostVO hostVO = mock(HostVO.class); + clusteredAgentManagerImpl._hostDao = _hostDao; + clusteredAgentManagerImpl.mgmtServiceConf = _mgmtServiceConf; + clusteredAgentManagerImpl._haMgr = mock(HighAvailabilityManagerImpl.class); + clusteredAgentManagerImpl._resourceMgr = mock(ResourceManagerImpl.class); + when(_mgmtServiceConf.getTimeout()).thenReturn(16000L); + when(hostVO.getId()).thenReturn(0L); + List hosts = new ArrayList<>(); + hosts.add(hostVO); + when(_hostDao.findAndUpdateDirectAgentToLoad(anyLong(), anyLong(), anyLong())).thenReturn(hosts); + AgentAttache agentAttache = mock(AgentAttache.class); + when(agentAttache.forForward()).thenReturn(Boolean.FALSE); + when(clusteredAgentManagerImpl.findAttache(0L)).thenReturn(agentAttache); + doReturn(Boolean.TRUE).when(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false); + clusteredAgentManagerImpl.scanDirectAgentToLoad(); + verify(clusteredAgentManagerImpl).investigate(agentAttache); + verify(clusteredAgentManagerImpl).loadDirectlyConnectedHost(hostVO, false); + } +} diff --git a/engine/orchestration/src/test/java/com/cloud/agent/manager/ConnectedAgentAttacheTest.java b/engine/orchestration/src/test/java/com/cloud/agent/manager/ConnectedAgentAttacheTest.java index 0b42b505668..66e6bbae5e2 100644 --- a/engine/orchestration/src/test/java/com/cloud/agent/manager/ConnectedAgentAttacheTest.java +++ b/engine/orchestration/src/test/java/com/cloud/agent/manager/ConnectedAgentAttacheTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.mock; import org.junit.Test; +import com.cloud.hypervisor.Hypervisor; import com.cloud.utils.nio.Link; public class ConnectedAgentAttacheTest { @@ -31,8 +32,8 @@ public class ConnectedAgentAttacheTest { Link link = mock(Link.class); - ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, link, false); - ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 0, "uuid", null, link, false); + ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, Hypervisor.HypervisorType.KVM, link, false); + ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 0, "uuid", null, Hypervisor.HypervisorType.KVM,link, false); assertTrue(agentAttache1.equals(agentAttache2)); } @@ -42,7 +43,7 @@ public class ConnectedAgentAttacheTest { Link link = mock(Link.class); - ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, link, false); + ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, Hypervisor.HypervisorType.KVM, link, false); assertFalse(agentAttache1.equals(null)); } @@ -53,8 +54,8 @@ public class ConnectedAgentAttacheTest { Link link1 = mock(Link.class); Link link2 = mock(Link.class); - ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, link1, false); - ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 0, "uuid", null, link2, false); + ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 0, "uuid", null, Hypervisor.HypervisorType.KVM, link1, false); + ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 0, "uuid", null, Hypervisor.HypervisorType.KVM, link2, false); assertFalse(agentAttache1.equals(agentAttache2)); } @@ -64,8 +65,8 @@ public class ConnectedAgentAttacheTest { Link link1 = mock(Link.class); - ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 1, "uuid", null, link1, false); - ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 2, "uuid", null, link1, false); + ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 1, "uuid", null, Hypervisor.HypervisorType.KVM, link1, false); + ConnectedAgentAttache agentAttache2 = new ConnectedAgentAttache(null, 2, "uuid", null, Hypervisor.HypervisorType.KVM, link1, false); assertFalse(agentAttache1.equals(agentAttache2)); } @@ -75,7 +76,7 @@ public class ConnectedAgentAttacheTest { Link link1 = mock(Link.class); - ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 1, "uuid", null, link1, false); + ConnectedAgentAttache agentAttache1 = new ConnectedAgentAttache(null, 1, "uuid", null, Hypervisor.HypervisorType.KVM, link1, false); assertFalse(agentAttache1.equals("abc")); } diff --git a/engine/orchestration/src/test/java/com/cloud/agent/manager/DirectAgentAttacheTest.java b/engine/orchestration/src/test/java/com/cloud/agent/manager/DirectAgentAttacheTest.java index 65e31c271a4..4ba276460e3 100644 --- a/engine/orchestration/src/test/java/com/cloud/agent/manager/DirectAgentAttacheTest.java +++ b/engine/orchestration/src/test/java/com/cloud/agent/manager/DirectAgentAttacheTest.java @@ -24,6 +24,7 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.hypervisor.Hypervisor; import com.cloud.resource.ServerResource; import java.util.UUID; @@ -42,7 +43,7 @@ public class DirectAgentAttacheTest { @Before public void setup() { - directAgentAttache = new DirectAgentAttache(_agentMgr, _id, _uuid, "myDirectAgentAttache", _resource, false); + directAgentAttache = new DirectAgentAttache(_agentMgr, _id, _uuid, "myDirectAgentAttache", Hypervisor.HypervisorType.KVM, _resource, false); MockitoAnnotations.initMocks(directAgentAttache); } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index 47f9b9f33e2..4f6329f81cb 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -23,11 +23,14 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +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.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Field; @@ -40,12 +43,18 @@ import java.util.Random; import java.util.UUID; import java.util.stream.Collectors; +import com.cloud.resource.ResourceManager; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl; +import org.apache.cloudstack.framework.extensions.dao.ExtensionDetailsDao; +import org.apache.cloudstack.framework.extensions.manager.ExtensionsManager; +import org.apache.cloudstack.framework.extensions.vo.ExtensionDetailsVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.collections.MapUtils; import org.junit.After; import org.junit.Assert; @@ -64,15 +73,20 @@ import org.springframework.test.util.ReflectionTestUtils; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Command; +import com.cloud.agent.api.PrepareExternalProvisioningAnswer; +import com.cloud.agent.api.RebootCommand; +import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.api.query.dao.UserVmJoinDao; import com.cloud.api.query.vo.UserVmJoinVO; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.Pod; import com.cloud.dc.dao.ClusterDao; @@ -85,12 +99,15 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.network.NetworkService; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.vpc.VpcVO; @@ -114,6 +131,7 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.AccountVO; @@ -126,8 +144,9 @@ import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; @RunWith(MockitoJUnitRunner.class) @@ -153,6 +172,9 @@ public class VirtualMachineManagerImplTest { @Mock private ServiceOfferingVO serviceOfferingMock; + @Mock + private SnapshotManager snapshotManagerMock; + @Mock private DiskOfferingVO diskOfferingMock; @@ -209,6 +231,8 @@ public class VirtualMachineManagerImplTest { @Mock private EntityManager _entityMgr; @Mock + private ResourceManager _resourceMgr; + @Mock private DeploymentPlanningManager _dpMgr; @Mock private HypervisorGuruManager _hvGuruMgr; @@ -217,11 +241,19 @@ public class VirtualMachineManagerImplTest { @Mock private ClusterDetailsDao _clusterDetailsDao; @Mock - private UserVmDetailsDao userVmDetailsDao; + private VMInstanceDetailsDao vmInstanceDetailsDao; @Mock private ItWorkDao _workDao; @Mock protected StateMachine2 _stateMachine; + @Mock + ExtensionsManager extensionsManager; + @Mock + ExtensionDetailsDao extensionDetailsDao; + @Mock + NicDao _nicsDao; + @Mock + NetworkService networkService; private ConfigDepotImpl configDepotImpl; private boolean updatedConfigKeyDepot = false; @@ -451,8 +483,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolProvided(storagePoolVoMock, volumeVoMock, Mockito.mock(StoragePoolVO.class)); - Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolVoMock, Mockito.times(0)).getId(); + verify(storagePoolVoMock).isManaged(); + verify(storagePoolVoMock, Mockito.times(0)).getId(); } @Test @@ -462,8 +494,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolProvided(storagePoolVoMock, volumeVoMock, Mockito.mock(StoragePoolVO.class)); - Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolVoMock, Mockito.times(0)).getId(); + verify(storagePoolVoMock).isManaged(); + verify(storagePoolVoMock, Mockito.times(0)).getId(); } @Test @@ -478,8 +510,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolProvided(storagePoolVoMock, volumeVoMock, storagePoolVoMock); - Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolVoMock, Mockito.times(2)).getId(); + verify(storagePoolVoMock).isManaged(); + verify(storagePoolVoMock, Mockito.times(2)).getId(); } @Test(expected = CloudRuntimeException.class) @@ -503,7 +535,7 @@ public class VirtualMachineManagerImplTest { Assert.assertTrue(volumeToPoolObjectMap.isEmpty()); - Mockito.verify(userDefinedVolumeToStoragePoolMap, times(0)).keySet(); + verify(userDefinedVolumeToStoragePoolMap, times(0)).keySet(); } @Test(expected = CloudRuntimeException.class) @@ -532,7 +564,7 @@ public class VirtualMachineManagerImplTest { assertFalse(volumeToPoolObjectMap.isEmpty()); assertEquals(storagePoolVoMock, volumeToPoolObjectMap.get(volumeVoMock)); - Mockito.verify(userDefinedVolumeToStoragePoolMap, times(1)).keySet(); + verify(userDefinedVolumeToStoragePoolMap, times(1)).keySet(); } @Test @@ -559,8 +591,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); - Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolHostDaoMock, Mockito.times(0)).findByPoolHost(anyLong(), anyLong()); + verify(storagePoolVoMock).isManaged(); + verify(storagePoolHostDaoMock, Mockito.times(0)).findByPoolHost(anyLong(), anyLong()); } @Test @@ -570,8 +602,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); - Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolHostDaoMock, Mockito.times(1)).findByPoolHost(storagePoolVoMockId, hostMockId); + verify(storagePoolVoMock).isManaged(); + verify(storagePoolHostDaoMock, Mockito.times(1)).findByPoolHost(storagePoolVoMockId, hostMockId); } @Test(expected = CloudRuntimeException.class) @@ -670,11 +702,11 @@ public class VirtualMachineManagerImplTest { Assert.assertTrue(poolList.isEmpty()); - Mockito.verify(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + verify(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.verify(storagePoolAllocatorMock2).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + verify(storagePoolAllocatorMock2).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.verify(storagePoolAllocatorMock3).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + verify(storagePoolAllocatorMock3).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); } @@ -732,8 +764,8 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.createStoragePoolMappingsForVolumes(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, allVolumes); Assert.assertTrue(volumeToPoolObjectMap.isEmpty()); - Mockito.verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); - Mockito.verify(virtualMachineManagerImpl).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, storagePoolVoMock); + verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); + verify(virtualMachineManagerImpl).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, storagePoolVoMock); } @Test @@ -751,9 +783,9 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.createStoragePoolMappingsForVolumes(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, allVolumes); Assert.assertTrue(volumeToPoolObjectMap.isEmpty()); - Mockito.verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); - Mockito.verify(virtualMachineManagerImpl).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, storagePoolVoMock); - Mockito.verify(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); + verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); + verify(virtualMachineManagerImpl).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, storagePoolVoMock); + verify(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); } @Test @@ -772,9 +804,9 @@ public class VirtualMachineManagerImplTest { assertFalse(volumeToPoolObjectMap.isEmpty()); assertEquals(storagePoolVoMock, volumeToPoolObjectMap.get(volumeVoMock)); - Mockito.verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); - Mockito.verify(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); - Mockito.verify(virtualMachineManagerImpl, Mockito.times(0)).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, + verify(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); + verify(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); + verify(virtualMachineManagerImpl, Mockito.times(0)).createVolumeToStoragePoolMappingIfPossible(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, volumeVoMock, storagePoolVoMock); } @@ -1091,7 +1123,7 @@ public class VirtualMachineManagerImplTest { when(cluster.getId()).thenReturn(1L); when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.CPU_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_cpu); when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_ram); - when(userVmDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); + when(vmInstanceDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); when(cluster_detail_cpu.getValue()).thenReturn("1.0"); when(cluster_detail_ram.getValue()).thenReturn("1.0"); doReturn(false).when(virtualMachineManagerImpl).areAllVolumesAllocated(Mockito.anyLong()); @@ -1187,7 +1219,7 @@ public class VirtualMachineManagerImplTest { when(cluster.getId()).thenReturn(1L); when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.CPU_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_cpu); when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_ram); - when(userVmDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); + when(vmInstanceDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); when(cluster_detail_cpu.getValue()).thenReturn("1.0"); when(cluster_detail_ram.getValue()).thenReturn("1.0"); doReturn(true).when(virtualMachineManagerImpl).areAllVolumesAllocated(Mockito.anyLong()); @@ -1304,4 +1336,309 @@ public class VirtualMachineManagerImplTest { Assert.assertEquals(manufacturer, to.getMetadataManufacturer()); Assert.assertEquals(product, to.getMetadataProductName()); } + + @Test + public void recreateCheckpointsKvmOnVmAfterMigrationTestReturnIfNotKvm() { + Mockito.doReturn(HypervisorType.VMware).when(vmInstanceMock).getHypervisorType(); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(volumeDaoMock, never()).findByInstance(Mockito.anyLong()); + } + + @Test + public void recreateCheckpointsKvmOnVmAfterMigrationTestReturnIfVolumesDoNotHaveCheckpoints() throws OperationTimedoutException, AgentUnavailableException { + Mockito.doReturn(HypervisorType.KVM).when(vmInstanceMock).getHypervisorType(); + Mockito.doReturn(new ArrayList()).when(virtualMachineManagerImpl).getVmVolumesWithCheckpointsToRecreate(Mockito.any()); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(agentManagerMock, never()).send(Mockito.anyLong(), (Command) any()); + } + + @Test (expected = CloudRuntimeException.class) + public void recreateCheckpointsKvmOnVmAfterMigrationTestAgentUnavailableThrowsCloudRuntimeExceptionAndEndsSnapshotChains() throws OperationTimedoutException, AgentUnavailableException { + Mockito.doReturn(HypervisorType.KVM).when(vmInstanceMock).getHypervisorType(); + Mockito.doReturn(List.of(new VolumeObjectTO())).when(virtualMachineManagerImpl).getVmVolumesWithCheckpointsToRecreate(Mockito.any()); + + Mockito.doThrow(new AgentUnavailableException(0)).when(agentManagerMock).send(Mockito.anyLong(), (Command) any()); + Mockito.doNothing().when(snapshotManagerMock).endSnapshotChainForVolume(Mockito.anyLong(), Mockito.any()); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(snapshotManagerMock, Mockito.times(1)).endSnapshotChainForVolume(Mockito.anyLong(),any()); + } + + @Test (expected = CloudRuntimeException.class) + public void recreateCheckpointsKvmOnVmAfterMigrationTestOperationTimedoutExceptionThrowsCloudRuntimeExceptionAndEndsSnapshotChains() throws OperationTimedoutException, AgentUnavailableException { + Mockito.doReturn(HypervisorType.KVM).when(vmInstanceMock).getHypervisorType(); + Mockito.doReturn(List.of(new VolumeObjectTO())).when(virtualMachineManagerImpl).getVmVolumesWithCheckpointsToRecreate(Mockito.any()); + + Mockito.doThrow(new OperationTimedoutException(null, 0, 0, 0, false)).when(agentManagerMock).send(Mockito.anyLong(), (Command) any()); + Mockito.doNothing().when(snapshotManagerMock).endSnapshotChainForVolume(Mockito.anyLong(), Mockito.any()); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(snapshotManagerMock, Mockito.times(1)).endSnapshotChainForVolume(Mockito.anyLong(),any()); + } + + @Test + public void recreateCheckpointsKvmOnVmAfterMigrationTestRecreationFails() throws OperationTimedoutException, AgentUnavailableException { + Mockito.doReturn(HypervisorType.KVM).when(vmInstanceMock).getHypervisorType(); + Mockito.doReturn(List.of(new VolumeObjectTO())).when(virtualMachineManagerImpl).getVmVolumesWithCheckpointsToRecreate(Mockito.any()); + + Mockito.doReturn(new com.cloud.agent.api.Answer(null, false, null)).when(agentManagerMock).send(Mockito.anyLong(), (Command) any()); + Mockito.doNothing().when(snapshotManagerMock).endSnapshotChainForVolume(Mockito.anyLong(), Mockito.any()); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(snapshotManagerMock, Mockito.times(1)).endSnapshotChainForVolume(Mockito.anyLong(),any()); + } + + @Test + public void recreateCheckpointsKvmOnVmAfterMigrationTestRecreationSucceeds() throws OperationTimedoutException, AgentUnavailableException { + Mockito.doReturn(HypervisorType.KVM).when(vmInstanceMock).getHypervisorType(); + Mockito.doReturn(List.of(new VolumeObjectTO())).when(virtualMachineManagerImpl).getVmVolumesWithCheckpointsToRecreate(Mockito.any()); + + Mockito.doReturn(new com.cloud.agent.api.Answer(null, true, null)).when(agentManagerMock).send(Mockito.anyLong(), (Command) any()); + + virtualMachineManagerImpl.recreateCheckpointsKvmOnVmAfterMigration(vmInstanceMock, 0); + + verify(snapshotManagerMock, never()).endSnapshotChainForVolume(Mockito.anyLong(),any()); + } + + @Test + public void updateStartCommandWithExternalDetails_nonExternalHypervisor_noAction() { + Host host = mock(Host.class); + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + StartCommand command = mock(StartCommand.class); + + when(host.getHypervisorType()).thenReturn(HypervisorType.KVM); + + virtualMachineManagerImpl.updateStartCommandWithExternalDetails(host, vmTO, command); + + verify(command, never()).setExternalDetails(any()); + } + + @Test + public void updateStartCommandWithExternalDetails_externalHypervisor_setsExternalDetails() { + Host host = mock(Host.class); + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + StartCommand command = mock(StartCommand.class); + NicTO nic = mock(NicTO.class); + + when(host.getHypervisorType()).thenReturn(HypervisorType.External); + when(vmTO.getExternalDetails()).thenReturn(new HashMap<>()); + when(vmTO.getNics()).thenReturn(new NicTO[]{nic}); + when(nic.isDefaultNic()).thenReturn(true); + when(networkService.getNicVlanValueForExternalVm(nic)).thenReturn("segmentName"); + when(extensionsManager.getExternalAccessDetails(eq(host), any())).thenReturn(new HashMap<>()); + + virtualMachineManagerImpl.updateStartCommandWithExternalDetails(host, vmTO, command); + + verify(command).setExternalDetails(any()); + } + + @Test + public void updateStopCommandForExternalHypervisorType_nonExternalHypervisor_noAction() { + VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + StopCommand stopCommand = mock(StopCommand.class); + + virtualMachineManagerImpl.updateStopCommandForExternalHypervisorType(HypervisorType.KVM, vmProfile, stopCommand); + + verify(stopCommand, never()).setExternalDetails(any()); + } + + @Test + public void updateStopCommandForExternalHypervisorType_externalHypervisor_setsExternalDetails() { + VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + StopCommand stopCommand = mock(StopCommand.class); + HostVO host = mock(HostVO.class); + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + when(vmProfile.getHostId()).thenReturn(1L); + when(hostDaoMock.findById(1L)).thenReturn(host); + when(stopCommand.getVirtualMachine()).thenReturn(vmTO); + when(vmTO.getExternalDetails()).thenReturn(new HashMap<>()); + when(extensionsManager.getExternalAccessDetails(eq(host), any())).thenReturn(new HashMap<>()); + doReturn(mock(VirtualMachineTO.class)).when(virtualMachineManagerImpl).toVmTO(any()); + virtualMachineManagerImpl.updateStopCommandForExternalHypervisorType(HypervisorType.External, vmProfile, stopCommand); + verify(stopCommand).setExternalDetails(any()); + } + + @Test + public void updateRebootCommandWithExternalDetails_nonExternalHypervisor_noAction() { + Host host = mock(Host.class); + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + RebootCommand rebootCmd = mock(RebootCommand.class); + when(host.getHypervisorType()).thenReturn(HypervisorType.KVM); + virtualMachineManagerImpl.updateRebootCommandWithExternalDetails(host, vmTO, rebootCmd); + verify(rebootCmd, never()).setExternalDetails(any()); + } + + @Test + public void updateRebootCommandWithExternalDetails_externalHypervisor_setsExternalDetails() { + Host host = mock(Host.class); + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + RebootCommand rebootCmd = mock(RebootCommand.class); + when(host.getHypervisorType()).thenReturn(HypervisorType.External); + when(vmTO.getExternalDetails()).thenReturn(new HashMap<>()); + when(extensionsManager.getExternalAccessDetails(eq(host), any())).thenReturn(new HashMap<>()); + virtualMachineManagerImpl.updateRebootCommandWithExternalDetails(host, vmTO, rebootCmd); + verify(rebootCmd).setExternalDetails(any()); + } + + @Test + public void updateExternalVmDetailsFromPrepareAnswer_nullDetails_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + UserVmVO userVmVO = mock(UserVmVO.class); + virtualMachineManagerImpl.updateExternalVmDetailsFromPrepareAnswer(vmTO, userVmVO, null); + verify(vmTO, never()).setDetails(any()); + verify(userVmVO, never()).setDetails(any()); + verify(userVmDaoMock, never()).saveDetails(any()); + } + + @Test + public void updateExternalVmDetailsFromPrepareAnswer_sameDetails_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + UserVmVO userVmVO = mock(UserVmVO.class); + Map details = new HashMap<>(); + when(vmTO.getDetails()).thenReturn(details); + virtualMachineManagerImpl.updateExternalVmDetailsFromPrepareAnswer(vmTO, userVmVO, details); + verify(vmTO, never()).setDetails(any()); + verify(userVmVO, never()).setDetails(any()); + verify(userVmDaoMock, never()).saveDetails(any()); + } + + @Test + public void updateExternalVmDataFromPrepareAnswer_vncPasswordUpdated_updatesPassword() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + UserVmVO userVmVO = mock(UserVmVO.class); + when(updatedTO.getVncPassword()).thenReturn("newPassword"); + when(vmTO.getVncPassword()).thenReturn("oldPassword"); + when(userVmDaoMock.findById(anyLong())).thenReturn(userVmVO); + virtualMachineManagerImpl.updateExternalVmDataFromPrepareAnswer(vmTO, updatedTO); + verify(userVmVO).setVncPassword("newPassword"); + verify(vmTO).setVncPassword("newPassword"); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_nullNics_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + when(vmTO.getNics()).thenReturn(null); + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + verify(_nicsDao, never()).findByUuid(anyString()); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_updatesNicsSuccessfully() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + NicTO nicTO = mock(NicTO.class); + NicTO updatedNicTO = mock(NicTO.class); + + when(vmTO.getNics()).thenReturn(new NicTO[]{nicTO}); + when(updatedTO.getNics()).thenReturn(new NicTO[]{updatedNicTO}); + when(nicTO.getNicUuid()).thenReturn("nic-uuid"); + when(nicTO.getMac()).thenReturn("mac-a"); + when(updatedNicTO.getNicUuid()).thenReturn("nic-uuid"); + when(updatedNicTO.getMac()).thenReturn("mac-b"); + when(_nicsDao.findByUuid("nic-uuid")).thenReturn(mock(NicVO.class)); + + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + + verify(_nicsDao).findByUuid("nic-uuid"); + verify(_nicsDao).update(anyLong(), any(NicVO.class)); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_noMatchingNicUuid_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + NicTO nicTO = mock(NicTO.class); + NicTO updatedNicTO = mock(NicTO.class); + + when(vmTO.getNics()).thenReturn(new NicTO[]{nicTO}); + when(updatedTO.getNics()).thenReturn(new NicTO[]{updatedNicTO}); + when(nicTO.getNicUuid()).thenReturn("nic-uuid"); + when(updatedNicTO.getNicUuid()).thenReturn("different-uuid"); + + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + + verify(_nicsDao, never()).findByUuid(anyString()); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_nullUpdatedNics_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + + when(vmTO.getNics()).thenReturn(new NicTO[]{mock(NicTO.class)}); + when(updatedTO.getNics()).thenReturn(null); + + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + + verify(_nicsDao, never()).findByUuid(anyString()); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_nullVmNics_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + + when(vmTO.getNics()).thenReturn(null); + when(updatedTO.getNics()).thenReturn(new NicTO[]{mock(NicTO.class)}); + + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + + verify(_nicsDao, never()).findByUuid(anyString()); + } + + @Test + public void updateExternalVmNicsFromPrepareAnswer_emptyNics_noAction() { + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + VirtualMachineTO updatedTO = mock(VirtualMachineTO.class); + + when(vmTO.getNics()).thenReturn(new NicTO[]{}); + when(updatedTO.getNics()).thenReturn(new NicTO[]{}); + + virtualMachineManagerImpl.updateExternalVmNicsFromPrepareAnswer(vmTO, updatedTO); + + verify(_nicsDao, never()).findByUuid(anyString()); + } + + @Test + public void processPrepareExternalProvisioning_nonExternalHypervisor_noAction() throws OperationTimedoutException, AgentUnavailableException { + Host host = mock(Host.class); + VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + VirtualMachineTemplate template = mock(VirtualMachineTemplate.class); + when(vmProfile.getTemplate()).thenReturn(template); + when(host.getHypervisorType()).thenReturn(HypervisorType.KVM); + virtualMachineManagerImpl.processPrepareExternalProvisioning(true, host, vmProfile, mock(DataCenter.class)); + verify(agentManagerMock, never()).send(anyLong(), any(Command.class)); + } + + @Test + public void processPrepareExternalProvisioning_externalHypervisor_sendsCommand() throws OperationTimedoutException, AgentUnavailableException { + Host host = mock(Host.class); + VirtualMachineProfile vmProfile = mock(VirtualMachineProfile.class); + VirtualMachineTemplate template = mock(VirtualMachineTemplate.class); + when(vmProfile.getTemplate()).thenReturn(template); + NicTO[] nics = new NicTO[]{mock(NicTO.class)}; + VirtualMachineTO vmTO = mock(VirtualMachineTO.class); + when(vmTO.getNics()).thenReturn(nics); + doReturn(vmTO).when(virtualMachineManagerImpl).toVmTO(vmProfile); + ExtensionDetailsVO detailsVO = mock(ExtensionDetailsVO.class); + when(host.getHypervisorType()).thenReturn(HypervisorType.External); + when(template.getExtensionId()).thenReturn(1L); + when(extensionDetailsDao.findDetail(eq(1L), eq(ApiConstants.ORCHESTRATOR_REQUIRES_PREPARE_VM))).thenReturn(detailsVO); + when(detailsVO.getValue()).thenReturn("true"); + PrepareExternalProvisioningAnswer answer = mock(PrepareExternalProvisioningAnswer.class); + when(answer.getResult()).thenReturn(true); + when(answer.getVirtualMachineTO()).thenReturn(vmTO); + when(agentManagerMock.send(anyLong(), any(Command.class))).thenReturn(answer); + virtualMachineManagerImpl.processPrepareExternalProvisioning(true, host, vmProfile, mock(DataCenter.class)); + verify(agentManagerMock).send(anyLong(), any(Command.class)); + } } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.java new file mode 100644 index 00000000000..4df14fe22f3 --- /dev/null +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachinePowerStateSyncImplTest.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 com.cloud.vm; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.PublishScope; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.vm.dao.VMInstanceDao; + +@RunWith(MockitoJUnitRunner.class) +public class VirtualMachinePowerStateSyncImplTest { + @Mock + MessageBus messageBus; + @Mock + VMInstanceDao instanceDao; + @Mock + HostDao hostDao; + + @InjectMocks + VirtualMachinePowerStateSyncImpl virtualMachinePowerStateSync = new VirtualMachinePowerStateSyncImpl(); + + @Before + public void setup() { + Mockito.lenient().when(instanceDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(VMInstanceVO.class)); + Mockito.lenient().when(hostDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(HostVO.class)); + } + + @Test + public void test_updateAndPublishVmPowerStates_emptyStates() { + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, new HashMap<>(), new Date()); + Mockito.verify(instanceDao, Mockito.never()).updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class)); + } + + @Test + public void test_updateAndPublishVmPowerStates_moreNotUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOff); + Map notUpdated = new HashMap<>(powerStates); + notUpdated.put(2L, VirtualMachine.PowerState.PowerOn); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(notUpdated); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.never()).publish(Mockito.nullable(String.class), Mockito.anyString(), + Mockito.any(PublishScope.class), Mockito.anyLong()); + } + + @Test + public void test_updateAndPublishVmPowerStates_allUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOff); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(new HashMap<>()); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.times(1)).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 1L); + } + + @Test + public void test_updateAndPublishVmPowerStates_partialUpdated() { + Map powerStates = new HashMap<>(); + powerStates.put(1L, VirtualMachine.PowerState.PowerOn); + powerStates.put(2L, VirtualMachine.PowerState.PowerOff); + Map notUpdated = new HashMap<>(); + notUpdated.put(2L, VirtualMachine.PowerState.PowerOff); + Mockito.when(instanceDao.updatePowerState(Mockito.anyMap(), Mockito.anyLong(), + Mockito.any(Date.class))).thenReturn(notUpdated); + virtualMachinePowerStateSync.updateAndPublishVmPowerStates(1L, powerStates, new Date()); + Mockito.verify(messageBus, Mockito.times(1)).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 1L); + Mockito.verify(messageBus, Mockito.never()).publish(null, + VirtualMachineManager.Topics.VM_POWER_STATE, + PublishScope.GLOBAL, + 2L); + } +} diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java new file mode 100644 index 00000000000..acd98e1cbff --- /dev/null +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/DataMigrationUtilityTest.java @@ -0,0 +1,88 @@ +// 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.engine.orchestration; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.VMTemplateVO; +import junit.framework.TestCase; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class DataMigrationUtilityTest extends TestCase { + + @Spy + private DataMigrationUtility dataMigrationUtility; + + @Mock + private VMTemplateVO templateVoMock; + + @Mock + private TemplateDataStoreVO templateDataStoreVoMock; + + private void prepareForShouldMigrateTemplateTests() { + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Ready); + Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(null); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenTemplateIsNotReady() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateDataStoreVoMock.getState()).thenReturn(ObjectInDataStoreStateMachine.State.Migrating); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenHypervisorTypeIsSimulator() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateVoMock.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.Simulator); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsFalseWhenTemplateHasParentTemplate() { + prepareForShouldMigrateTemplateTests(); + Mockito.when(templateVoMock.getParentTemplateId()).thenReturn(1L); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertFalse(result); + } + + @Test + public void shouldMigrateTemplateTestReturnsTrueWhenTemplateIsReadyAndDoesNotHaveParentTemplateAndHypervisorTypeIsNotSimulator() { + prepareForShouldMigrateTemplateTests(); + + boolean result = dataMigrationUtility.shouldMigrateTemplate(templateDataStoreVoMock, templateVoMock); + + Assert.assertTrue(result); + } +} diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index 58746a9a6cf..064ca44c69a 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -891,7 +891,7 @@ public class NetworkOrchestratorTest extends TestCase { boolean shutdownNetworkStatus = testOrchestrator.shutdownNetwork(networkId, reservationContext, false); Assert.assertFalse(shutdownNetworkStatus); - verify(network, times(3)).getState(); + verify(network).getState(); verify(testOrchestrator._networksDao, times(1)).acquireInLockTable(networkId, NetworkLockTimeout.value()); verify(testOrchestrator._networksDao, times(1)).releaseFromLockTable(networkId); } diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java index a37845c86c2..817ff02ef74 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java @@ -18,13 +18,32 @@ package org.apache.cloudstack.engine.orchestration; import java.util.ArrayList; import java.util.Date; +import java.util.List; +import java.util.Set; +import java.lang.reflect.Field; +import com.cloud.configuration.Resource; +import com.cloud.deploy.DeploymentClusterPlanner; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.StorageAccessException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering; +import com.cloud.storage.ScopeType; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage; import com.cloud.storage.Volume; +import com.cloud.storage.Volume.Type; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; +import com.cloud.user.ResourceLimitService; +import com.cloud.uservm.UserVm; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; +import com.cloud.utils.Pair; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; @@ -32,6 +51,17 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.framework.config.ConfigDepot; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.secret.PassphraseVO; +import org.apache.cloudstack.secret.dao.PassphraseDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.commons.lang3.ObjectUtils; import org.junit.Assert; import org.junit.Before; @@ -45,14 +75,9 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; -import com.cloud.configuration.Resource; -import com.cloud.exception.StorageAccessException; -import com.cloud.host.Host; -import com.cloud.host.HostVO; -import com.cloud.storage.VolumeVO; -import com.cloud.storage.Volume.Type; -import com.cloud.user.ResourceLimitService; -import com.cloud.utils.exception.CloudRuntimeException; +import static org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService.VolumeAllocationAlgorithm; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; @RunWith(MockitoJUnitRunner.class) public class VolumeOrchestratorTest { @@ -65,6 +90,24 @@ public class VolumeOrchestratorTest { protected VolumeDataFactory volumeDataFactory; @Mock protected VolumeDao volumeDao; + @Mock + protected PassphraseDao passphraseDao; + @Mock + protected PrimaryDataStoreDao storagePoolDao; + @Mock + protected EntityManager entityMgr; + @Mock + ConfigDepot configDepot; + @Mock + ConfigurationDao configurationDao; + + + @Mock + private SnapshotDataStoreDao snapshotDataStoreDaoMock; + + @Mock + private ImageStoreDao imageStoreDaoMock; + @Spy @InjectMocks @@ -72,6 +115,9 @@ public class VolumeOrchestratorTest { private static final Long DEFAULT_ACCOUNT_PS_RESOURCE_COUNT = 100L; private Long accountPSResourceCount; + private static final long MOCK_VM_ID = 202L; + private static final long MOCK_POOL_ID = 303L; + private static final String MOCK_VM_NAME = "Test-VM"; @Before public void setUp() throws Exception { @@ -208,4 +254,390 @@ public class VolumeOrchestratorTest { Mockito.verify(volume, Mockito.times(1)).setChainInfo(chainInfo); Mockito.verify(volume, Mockito.times(1)).setState(Volume.State.Ready); } + + @Test + public void testAllocateDuplicateVolumeVOBasic() { + Volume oldVol = Mockito.mock(Volume.class); + Mockito.when(oldVol.getVolumeType()).thenReturn(Volume.Type.ROOT); + Mockito.when(oldVol.getName()).thenReturn("testVol"); + Mockito.when(oldVol.getDataCenterId()).thenReturn(1L); + Mockito.when(oldVol.getDomainId()).thenReturn(2L); + Mockito.when(oldVol.getAccountId()).thenReturn(3L); + Mockito.when(oldVol.getDiskOfferingId()).thenReturn(4L); + Mockito.when(oldVol.getProvisioningType()).thenReturn(Storage.ProvisioningType.THIN); + Mockito.when(oldVol.getSize()).thenReturn(10L); + Mockito.when(oldVol.getMinIops()).thenReturn(100L); + Mockito.when(oldVol.getMaxIops()).thenReturn(200L); + Mockito.when(oldVol.get_iScsiName()).thenReturn("iqn.test"); + Mockito.when(oldVol.getTemplateId()).thenReturn(5L); + Mockito.when(oldVol.getDeviceId()).thenReturn(1L); + Mockito.when(oldVol.getInstanceId()).thenReturn(6L); + Mockito.when(oldVol.isRecreatable()).thenReturn(false); + Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.QCOW2); + Mockito.when(oldVol.getPassphraseId()).thenReturn(null); // no encryption + + VolumeVO persistedVol = Mockito.mock(VolumeVO.class); + Mockito.when(volumeDao.persist(Mockito.any(VolumeVO.class))).thenReturn(persistedVol); + + VolumeVO result = volumeOrchestrator.allocateDuplicateVolumeVO(oldVol, null, null); + assertNotNull(result); + Mockito.verify(volumeDao, Mockito.times(1)).persist(Mockito.any(VolumeVO.class)); + } + + @Test + public void testAllocateDuplicateVolumeVOWithEncryption() { + Volume oldVol = Mockito.mock(Volume.class); + Mockito.when(oldVol.getVolumeType()).thenReturn(Volume.Type.ROOT); + Mockito.when(oldVol.getName()).thenReturn("secureVol"); + Mockito.when(oldVol.getDataCenterId()).thenReturn(1L); + Mockito.when(oldVol.getDomainId()).thenReturn(2L); + Mockito.when(oldVol.getAccountId()).thenReturn(3L); + Mockito.when(oldVol.getDiskOfferingId()).thenReturn(4L); + Mockito.when(oldVol.getProvisioningType()).thenReturn(Storage.ProvisioningType.THIN); + Mockito.when(oldVol.getSize()).thenReturn(10L); + Mockito.when(oldVol.getMinIops()).thenReturn(100L); + Mockito.when(oldVol.getMaxIops()).thenReturn(200L); + Mockito.when(oldVol.get_iScsiName()).thenReturn("iqn.secure"); + Mockito.when(oldVol.getTemplateId()).thenReturn(5L); + Mockito.when(oldVol.getDeviceId()).thenReturn(2L); + Mockito.when(oldVol.getInstanceId()).thenReturn(7L); + Mockito.when(oldVol.isRecreatable()).thenReturn(true); + Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.RAW); + Mockito.when(oldVol.getPassphraseId()).thenReturn(42L); + + PassphraseVO passphrase = Mockito.mock(PassphraseVO.class); + Mockito.when(passphrase.getId()).thenReturn(999L); + Mockito.when(passphraseDao.persist(Mockito.any())).thenReturn(passphrase); + + VolumeVO persistedVol = Mockito.mock(VolumeVO.class); + Mockito.when(volumeDao.persist(Mockito.any())).thenReturn(persistedVol); + + VolumeVO result = volumeOrchestrator.allocateDuplicateVolumeVO(oldVol, null, null); + assertNotNull(result); + Mockito.verify(passphraseDao).persist(Mockito.any(PassphraseVO.class)); + Mockito.verify(volumeDao).persist(Mockito.any()); + } + + @Test + public void testAllocateDuplicateVolumeVOWithTemplateOverride() { + Volume oldVol = Mockito.mock(Volume.class); + Mockito.when(oldVol.getVolumeType()).thenReturn(Volume.Type.ROOT); + Mockito.when(oldVol.getName()).thenReturn("tmplVol"); + Mockito.when(oldVol.getDataCenterId()).thenReturn(1L); + Mockito.when(oldVol.getDomainId()).thenReturn(2L); + Mockito.when(oldVol.getAccountId()).thenReturn(3L); + Mockito.when(oldVol.getDiskOfferingId()).thenReturn(4L); + Mockito.when(oldVol.getProvisioningType()).thenReturn(Storage.ProvisioningType.THIN); + Mockito.when(oldVol.getSize()).thenReturn(20L); + Mockito.when(oldVol.getMinIops()).thenReturn(50L); + Mockito.when(oldVol.getMaxIops()).thenReturn(250L); + Mockito.when(oldVol.get_iScsiName()).thenReturn("iqn.tmpl"); + + VolumeVO persistedVol = Mockito.mock(VolumeVO.class); + Mockito.when(volumeDao.persist(Mockito.any())).thenReturn(persistedVol); + + PassphraseVO mockPassPhrase = Mockito.mock(PassphraseVO.class); + Mockito.when(passphraseDao.persist(Mockito.any())).thenReturn(mockPassPhrase); + + VolumeVO result = volumeOrchestrator.allocateDuplicateVolumeVO(oldVol, null, 222L); + assertNotNull(result); + } + + @Test + public void testAllocateDuplicateVolumeVOEncryptionFromOldVolumeOnly() { + Volume oldVol = Mockito.mock(Volume.class); + Mockito.when(oldVol.getVolumeType()).thenReturn(Volume.Type.ROOT); + Mockito.when(oldVol.getName()).thenReturn("vol-old"); + Mockito.when(oldVol.getDataCenterId()).thenReturn(1L); + Mockito.when(oldVol.getDomainId()).thenReturn(2L); + Mockito.when(oldVol.getAccountId()).thenReturn(3L); + Mockito.when(oldVol.getDiskOfferingId()).thenReturn(4L); + Mockito.when(oldVol.getProvisioningType()).thenReturn(Storage.ProvisioningType.SPARSE); + Mockito.when(oldVol.getSize()).thenReturn(30L); + Mockito.when(oldVol.getMinIops()).thenReturn(10L); + Mockito.when(oldVol.getMaxIops()).thenReturn(500L); + Mockito.when(oldVol.get_iScsiName()).thenReturn("iqn.old"); + Mockito.when(oldVol.getTemplateId()).thenReturn(123L); + Mockito.when(oldVol.getDeviceId()).thenReturn(1L); + Mockito.when(oldVol.getInstanceId()).thenReturn(100L); + Mockito.when(oldVol.isRecreatable()).thenReturn(false); + Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.RAW); + + DiskOffering diskOffering = Mockito.mock(DiskOffering.class); + Mockito.when(diskOffering.getEncrypt()).thenReturn(false); // explicitly disables encryption + + VolumeVO persistedVol = Mockito.mock(VolumeVO.class); + Mockito.when(volumeDao.persist(Mockito.any())).thenReturn(persistedVol); + + VolumeVO result = volumeOrchestrator.allocateDuplicateVolumeVO(oldVol, diskOffering, null); + assertNotNull(result); + Mockito.verify(volumeDao).persist(Mockito.any()); + } + + @Test + public void testVolumeOnSharedStoragePoolTrue() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getPoolId()).thenReturn(MOCK_POOL_ID); + + StoragePoolVO pool = Mockito.mock(StoragePoolVO.class); + Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); // Shared scope + Mockito.when(storagePoolDao.findById(MOCK_POOL_ID)).thenReturn(pool); + + assertTrue(volumeOrchestrator.volumeOnSharedStoragePool(volume)); + } + + @Test + public void testVolumeOnSharedStoragePoolFalseHostScope() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getPoolId()).thenReturn(MOCK_POOL_ID); + + StoragePoolVO pool = Mockito.mock(StoragePoolVO.class); + Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST); // Local scope + Mockito.when(storagePoolDao.findById(MOCK_POOL_ID)).thenReturn(pool); + + Assert.assertFalse(volumeOrchestrator.volumeOnSharedStoragePool(volume)); + } + + @Test + public void testVolumeOnSharedStoragePoolFalseNoPool() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getPoolId()).thenReturn(null); // No pool associated + + Assert.assertFalse(volumeOrchestrator.volumeOnSharedStoragePool(volume)); + Mockito.verify(storagePoolDao, Mockito.never()).findById(Mockito.anyLong()); + } + + @Test + public void testVolumeOnSharedStoragePoolFalsePoolNotFound() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getPoolId()).thenReturn(MOCK_POOL_ID); + + Mockito.when(storagePoolDao.findById(MOCK_POOL_ID)).thenReturn(null); // Pool not found in DB + + Assert.assertFalse(volumeOrchestrator.volumeOnSharedStoragePool(volume)); + } + + + @Test + public void testVolumeInactiveNoVmId() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(null); + assertTrue(volumeOrchestrator.volumeInactive(volume)); + Mockito.verify(entityMgr, Mockito.never()).findById(Mockito.eq(UserVm.class), Mockito.anyLong()); + } + + @Test + public void testVolumeInactiveVmNotFound() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + Mockito.when(entityMgr.findById(UserVm.class, MOCK_VM_ID)).thenReturn(null); + assertTrue(volumeOrchestrator.volumeInactive(volume)); + } + + @Test + public void testVolumeInactiveVmStopped() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Stopped); + Mockito.when(entityMgr.findById(UserVm.class, MOCK_VM_ID)).thenReturn(vm); + assertTrue(volumeOrchestrator.volumeInactive(volume)); + } + + @Test + public void testVolumeInactiveVmDestroyed() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Destroyed); + Mockito.when(entityMgr.findById(UserVm.class, MOCK_VM_ID)).thenReturn(vm); + assertTrue(volumeOrchestrator.volumeInactive(volume)); + } + + @Test + public void testVolumeInactiveVmRunning() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + UserVm vm = Mockito.mock(UserVm.class); + Mockito.when(vm.getState()).thenReturn(VirtualMachine.State.Running); // Active state + Mockito.when(entityMgr.findById(UserVm.class, MOCK_VM_ID)).thenReturn(vm); + Assert.assertFalse(volumeOrchestrator.volumeInactive(volume)); + } + + @Test + public void testGetVmNameOnVolumeNoVmId() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(null); + Assert.assertNull(volumeOrchestrator.getVmNameOnVolume(volume)); + Mockito.verify(entityMgr, Mockito.never()).findById(Mockito.eq(VirtualMachine.class), Mockito.anyLong()); + } + + @Test + public void testGetVmNameOnVolumeVmNotFound() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + Mockito.when(entityMgr.findById(VirtualMachine.class, MOCK_VM_ID)).thenReturn(null); + Assert.assertNull(volumeOrchestrator.getVmNameOnVolume(volume)); + } + + @Test + public void testGetVmNameOnVolumeSuccess() { + VolumeVO volume = Mockito.mock(VolumeVO.class); + Mockito.when(volume.getInstanceId()).thenReturn(MOCK_VM_ID); + VirtualMachine vm = Mockito.mock(VirtualMachine.class); + Mockito.when(vm.getInstanceName()).thenReturn(MOCK_VM_NAME); + Mockito.when(entityMgr.findById(VirtualMachine.class, MOCK_VM_ID)).thenReturn(vm); + Assert.assertEquals(MOCK_VM_NAME, volumeOrchestrator.getVmNameOnVolume(volume)); + } + + @Test + public void testValidateVolumeSizeRangeValid() throws Exception { + overrideDefaultConfigValue(VolumeOrchestrator.MaxVolumeSize, "2000"); + assertTrue(volumeOrchestrator.validateVolumeSizeRange(1024 * 1024 * 1024)); // 1 GiB + assertTrue(volumeOrchestrator.validateVolumeSizeRange(2000 * 1024 * 1024 * 1024)); // 2 TiB + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateVolumeSizeRangeTooSmall() { + volumeOrchestrator.validateVolumeSizeRange(1024L); // Less than 1GiB + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateVolumeSizeRangeNegative() { + volumeOrchestrator.validateVolumeSizeRange(-10); // Negative size + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateVolumeSizeRangeTooLarge() throws Exception { + overrideDefaultConfigValue(VolumeOrchestrator.MaxVolumeSize, "100L"); + volumeOrchestrator.validateVolumeSizeRange(101); + } + + @Test + public void testCanVmRestartOnAnotherServerAllShared() { + VolumeVO vol1 = Mockito.mock(VolumeVO.class); + VolumeVO vol2 = Mockito.mock(VolumeVO.class); + Mockito.when(vol1.getPoolId()).thenReturn(10L); + Mockito.when(vol2.getPoolId()).thenReturn(20L); + Mockito.when(vol1.isRecreatable()).thenReturn(false); + Mockito.when(vol2.isRecreatable()).thenReturn(false); + + + StoragePoolVO pool1 = Mockito.mock(StoragePoolVO.class); + StoragePoolVO pool2 = Mockito.mock(StoragePoolVO.class); + Mockito.when(pool1.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); // Shared + Mockito.when(pool2.getPoolType()).thenReturn(Storage.StoragePoolType.RBD); // Shared + + Mockito.when(volumeDao.findCreatedByInstance(MOCK_VM_ID)).thenReturn(List.of(vol1, vol2)); + Mockito.when(storagePoolDao.findById(10L)).thenReturn(pool1); + Mockito.when(storagePoolDao.findById(20L)).thenReturn(pool2); + + + assertTrue(volumeOrchestrator.canVmRestartOnAnotherServer(MOCK_VM_ID)); + } + + @Test + public void testCanVmRestartOnAnotherServerOneLocalNotRecreatable() { + VolumeVO vol1 = Mockito.mock(VolumeVO.class); + VolumeVO vol2 = Mockito.mock(VolumeVO.class); // Local, not recreatable + Mockito.when(vol1.getPoolId()).thenReturn(10L); + Mockito.when(vol2.getPoolId()).thenReturn(30L); + Mockito.when(vol1.isRecreatable()).thenReturn(false); + Mockito.when(vol2.isRecreatable()).thenReturn(false); // Not recreatable + + StoragePoolVO pool1 = Mockito.mock(StoragePoolVO.class); + StoragePoolVO pool2 = Mockito.mock(StoragePoolVO.class); + Mockito.when(pool1.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); // Shared + Mockito.when(pool2.getPoolType()).thenReturn(Storage.StoragePoolType.LVM); // Local + + Mockito.when(volumeDao.findCreatedByInstance(MOCK_VM_ID)).thenReturn(List.of(vol1, vol2)); + Mockito.when(storagePoolDao.findById(10L)).thenReturn(pool1); + Mockito.when(storagePoolDao.findById(30L)).thenReturn(pool2); + + Assert.assertFalse("VM restart should be false if a non-recreatable local disk exists", + volumeOrchestrator.canVmRestartOnAnotherServer(MOCK_VM_ID)); + } + + @Test + public void testCanVmRestartOnAnotherServerOneLocalRecreatable() { + VolumeVO vol1 = Mockito.mock(VolumeVO.class); + VolumeVO vol2 = Mockito.mock(VolumeVO.class); // Local, but recreatable + Mockito.when(vol1.getPoolId()).thenReturn(10L); + Mockito.when(vol2.getPoolId()).thenReturn(30L); + Mockito.when(vol1.isRecreatable()).thenReturn(false); + Mockito.when(vol2.isRecreatable()).thenReturn(true); // Recreatable + + StoragePoolVO pool1 = Mockito.mock(StoragePoolVO.class); + StoragePoolVO pool2 = Mockito.mock(StoragePoolVO.class); + Mockito.when(pool1.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); // Shared + + Mockito.when(volumeDao.findCreatedByInstance(MOCK_VM_ID)).thenReturn(List.of(vol1, vol2)); + Mockito.when(storagePoolDao.findById(10L)).thenReturn(pool1); + Mockito.when(storagePoolDao.findById(30L)).thenReturn(pool2); + + assertTrue("VM restart should be true if local disk is recreatable", + volumeOrchestrator.canVmRestartOnAnotherServer(MOCK_VM_ID)); + } + + private void overrideDefaultConfigValue(final ConfigKey configKey, final String value) throws IllegalAccessException, NoSuchFieldException { + final Field f = ConfigKey.class.getDeclaredField("_defaultValue"); + f.setAccessible(true); + f.set(configKey, value); + } + + @Test + public void testStart() throws Exception { + Mockito.when(configDepot.isNewConfig(VolumeAllocationAlgorithm)).thenReturn(true); + overrideDefaultConfigValue(DeploymentClusterPlanner.VmAllocationAlgorithm, "firstfit"); + Mockito.when(configurationDao.update(Mockito.anyString(), Mockito.anyString())).thenReturn(true); + volumeOrchestrator.start(); + } + + @Test + public void testConfigKeys() { + assertTrue(volumeOrchestrator.getConfigKeys().length > 0); + } + + @Test + public void getVolumeCheckpointPathsAndImageStoreUrlsTestReturnEmptyListsIfNotKVM() { + Pair, Set> result = volumeOrchestrator.getVolumeCheckpointPathsAndImageStoreUrls(0, Hypervisor.HypervisorType.VMware); + + Assert.assertTrue(result.first().isEmpty()); + Assert.assertTrue(result.second().isEmpty()); + } + + @Test + public void getVolumeCheckpointPathsAndImageStoreUrlsTestReturnCheckpointIfKVM() { + SnapshotDataStoreVO snapshotDataStoreVO = new SnapshotDataStoreVO(); + snapshotDataStoreVO.setKvmCheckpointPath("Test"); + snapshotDataStoreVO.setRole(DataStoreRole.Primary); + + Mockito.doReturn(List.of(snapshotDataStoreVO)).when(snapshotDataStoreDaoMock).listReadyByVolumeIdAndCheckpointPathNotNull(Mockito.anyLong()); + + Pair, Set> result = volumeOrchestrator.getVolumeCheckpointPathsAndImageStoreUrls(0, Hypervisor.HypervisorType.KVM); + + Assert.assertEquals("Test", result.first().get(0)); + Assert.assertTrue(result.second().isEmpty()); + } + + @Test + public void getVolumeCheckpointPathsAndImageStoreUrlsTestReturnCheckpointIfKVMAndImageStore() { + SnapshotDataStoreVO snapshotDataStoreVO = new SnapshotDataStoreVO(); + snapshotDataStoreVO.setKvmCheckpointPath("Test"); + snapshotDataStoreVO.setRole(DataStoreRole.Image); + snapshotDataStoreVO.setDataStoreId(13); + + Mockito.doReturn(List.of(snapshotDataStoreVO)).when(snapshotDataStoreDaoMock).listReadyByVolumeIdAndCheckpointPathNotNull(Mockito.anyLong()); + + ImageStoreVO imageStoreVO = new ImageStoreVO(); + imageStoreVO.setUrl("URL"); + Mockito.doReturn(imageStoreVO).when(imageStoreDaoMock).findById(Mockito.anyLong()); + + Pair, Set> result = volumeOrchestrator.getVolumeCheckpointPathsAndImageStoreUrls(0, Hypervisor.HypervisorType.KVM); + + Assert.assertEquals("Test", result.first().get(0)); + Assert.assertTrue(result.second().contains("URL")); + Assert.assertEquals(1, result.second().size()); + } + } diff --git a/engine/pom.xml b/engine/pom.xml index 1df7d84284f..b771138dc8c 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -25,7 +25,7 @@ org.apache.cloudstack cloudstack - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT ../pom.xml diff --git a/engine/schema/pom.xml b/engine/schema/pom.xml index 8238da84835..2f216884c8b 100644 --- a/engine/schema/pom.xml +++ b/engine/schema/pom.xml @@ -24,7 +24,7 @@ org.apache.cloudstack cloud-engine - 4.21.0.0-SNAPSHOT + 4.22.0.0-SNAPSHOT ../pom.xml @@ -106,7 +106,7 @@ templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-xen") templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-ovm") templateList.add("systemvmtemplate-${csVersion}.${patch}-x86_64-hyperv") - File file = new File("./engine/schema/dist/systemvm-templates/md5sum.txt") + File file = new File("./engine/schema/dist/systemvm-templates/sha512sum.txt") def lines = file.readLines() for (template in templateList) { def data = lines.findAll { it.contains(template) } @@ -135,7 +135,7 @@ wget - ${project.systemvm.template.location}/${cs.version}/md5sum.txt + ${project.systemvm.template.location}/${cs.version}/sha512sum.txt ${basedir}/dist/systemvm-templates/ true true @@ -165,6 +165,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + +
@@ -196,7 +205,7 @@ true ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-kvm.qcow2.bz2 ${basedir}/dist/systemvm-templates/ - ${kvm.checksum} + ${kvm.checksum} @@ -232,7 +241,7 @@ true ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-vmware.ova ${basedir}/dist/systemvm-templates/ - ${vmware.checksum} + ${vmware.checksum} @@ -268,7 +277,7 @@ true ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-xen.vhd.bz2 ${basedir}/dist/systemvm-templates/ - ${xen.checksum} + ${xen.checksum} @@ -304,7 +313,7 @@ true ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-ovm.raw.bz2 ${basedir}/dist/systemvm-templates/ - ${ovm.checksum} + ${ovm.checksum} @@ -340,7 +349,7 @@ true ${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-x86_64-hyperv.vhd.zip ${basedir}/dist/systemvm-templates/ - ${hyperv.checksum} + ${hyperv.checksum} diff --git a/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java b/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java index cd62935f17e..fb2d61d8e11 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java +++ b/engine/schema/src/main/java/com/cloud/capacity/CapacityVO.java @@ -254,6 +254,8 @@ public class CapacityVO implements Capacity { capacityNames.put(CAPACITY_TYPE_GPU, "GPU"); capacityNames.put(CAPACITY_TYPE_CPU_CORE, "CPU_CORE"); capacityNames.put(CAPACITY_TYPE_VIRTUAL_NETWORK_IPV6_SUBNET, "VIRTUAL_NETWORK_IPV6_SUBNET"); + capacityNames.put(CAPACITY_TYPE_BACKUP_STORAGE, "BACKUP_STORAGE"); + capacityNames.put(CAPACITY_TYPE_OBJECT_STORAGE, "OBJECT_STORAGE"); } public static String getCapacityName (Short capacityType) { diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java index 1bb79ce417a..42313efa512 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java @@ -30,7 +30,7 @@ public interface CapacityDao extends GenericDao { List listByHostIdTypes(Long hostId, List capacityTypes); - List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone); + List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, boolean isZone); List listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType); @@ -48,7 +48,7 @@ public interface CapacityDao extends GenericDao { List findFilteredCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, List hostIds, List poolIds); - List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType); + List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam); Pair, Map> orderPodsByAggregateCapacity(long zoneId, short capacityType); @@ -64,5 +64,11 @@ public interface CapacityDao extends GenericDao { float findClusterConsumption(Long clusterId, short capacityType, long computeRequested); - List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityType); + + List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes); + + List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes); + + List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes); } diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index 5e7eee4566c..91949bded50 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -204,9 +204,9 @@ public class CapacityDaoImpl extends GenericDaoBase implements "(CASE WHEN ISNULL(service_offering.speed) THEN custom_speed.value ELSE service_offering.speed end) AS speed, " + "(CASE WHEN ISNULL(service_offering.ram_size) THEN custom_ram_size.value ELSE service_offering.ram_size end) AS ram_size " + "FROM vm_instance vi LEFT JOIN service_offering ON(((vi.service_offering_id = service_offering.id))) " + - "LEFT JOIN user_vm_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + - "LEFT JOIN user_vm_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + - "LEFT JOIN user_vm_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; + "LEFT JOIN vm_instance_details custom_cpu ON(((custom_cpu.vm_id = vi.id) AND (custom_cpu.name = 'CpuNumber'))) " + + "LEFT JOIN vm_instance_details custom_speed ON(((custom_speed.vm_id = vi.id) AND (custom_speed.name = 'CpuSpeed'))) " + + "LEFT JOIN vm_instance_details custom_ram_size ON(((custom_ram_size.vm_id = vi.id) AND (custom_ram_size.name = 'memory'))) "; private static final String WHERE_STATE_IS_NOT_DESTRUCTIVE = "WHERE ISNULL(vi.removed) AND vi.state NOT IN ('Destroyed', 'Error', 'Expunging')"; @@ -684,7 +684,7 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) { + public List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, boolean isZone) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); @@ -1028,10 +1028,11 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public List orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ + public Pair, Map> orderHostsByFreeCapacity(Long zoneId, Long clusterId, short capacityTypeForOrdering){ TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; - List result = new ArrayList(); + List result = new ArrayList<>(); + Map hostCapacityMap = new HashMap<>(); StringBuilder sql = new StringBuilder(ORDER_HOSTS_BY_FREE_CAPACITY_PART1); if (zoneId != null) { sql.append(" AND data_center_id = ?"); @@ -1054,9 +1055,11 @@ public class CapacityDaoImpl extends GenericDaoBase implements ResultSet rs = pstmt.executeQuery(); while (rs.next()) { - result.add(rs.getLong(1)); + Long hostId = rs.getLong(1); + result.add(hostId); + hostCapacityMap.put(hostId, rs.getDouble(2)); } - return result; + return new Pair<>(result, hostCapacityMap); } catch (SQLException e) { throw new CloudRuntimeException("DB Exception on: " + sql, e); } catch (Throwable e) { @@ -1065,7 +1068,65 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType) { + public List listHostCapacityByCapacityTypes(Long zoneId, Long clusterId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodCapacityByCapacityTypes(Long zoneId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listClusterCapacityByCapacityTypes(Long zoneId, Long podId, List capacityTypes) { + SearchBuilder sb = createSearchBuilder(); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); + sb.and("capacityTypes", sb.entity().getCapacityType(), SearchCriteria.Op.IN); + sb.and("capacityState", sb.entity().getCapacityState(), Op.EQ); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("capacityState", "Enabled"); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (podId != null) { + sc.setParameters("podId", podId); + } + sc.setParameters("capacityTypes", capacityTypes.toArray()); + return listBy(sc); + } + + @Override + public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java index 51b7f62f56d..841447de5fd 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java +++ b/engine/schema/src/main/java/com/cloud/configuration/ManagementServiceConfiguration.java @@ -21,7 +21,7 @@ import org.apache.cloudstack.framework.config.Configurable; public interface ManagementServiceConfiguration extends Configurable { ConfigKey PingInterval = new ConfigKey("Advanced", Integer.class, "ping.interval", "60", - "Interval to send application level pings to make sure the connection is still working", false); + "Interval in seconds to send application level pings to make sure the connection is still working", false); ConfigKey PingTimeout = new ConfigKey("Advanced", Float.class, "ping.timeout", "2.5", "Multiplier to ping.interval before announcing an agent has timed out", true); public int getPingInterval(); diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java index b978cc04bfa..ebacbd8b3e5 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDao.java @@ -62,20 +62,6 @@ public interface ResourceCountDao extends GenericDao { long removeEntriesByOwner(long ownerId, ResourceOwnerType ownerType); - /** - * Counts the number of CPU cores allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countCpuNumberAllocatedToAccount(long accountId); - - /** - * Counts the amount of memory allocated for the given account. - * - * Side note: This method is not using the "resource_count" table. It is executing the actual count instead. - */ - long countMemoryAllocatedToAccount(long accountId); - void removeResourceCountsForNonMatchingTags(Long ownerId, ResourceOwnerType ownerType, List types, List tags); List lockRows(Set ids); diff --git a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java index 65d7fed2d1a..2083fb422d2 100644 --- a/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/configuration/dao/ResourceCountDaoImpl.java @@ -336,22 +336,9 @@ public class ResourceCountDaoImpl extends GenericDaoBase + " END)) as total " + " from vm_instance vm " + " join service_offering so on so.id = vm.service_offering_id " - + " left join user_vm_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + + " left join vm_instance_details vmd on vmd.vm_id = vm.id and vmd.name = '%s' " + " where vm.type = 'User' and state not in ('Destroyed', 'Error', 'Expunging') and display_vm = true and account_id = ? "; - @Override - public long countCpuNumberAllocatedToAccount(long accountId) { - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, ResourceType.cpu, ResourceType.cpu, "cpuNumber"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - - @Override - public long countMemoryAllocatedToAccount(long accountId) { - String serviceOfferingRamSizeField = "ram_size"; - String sqlCountCpuNumberAllocatedToAccount = String.format(baseSqlCountComputingResourceAllocatedToAccount, serviceOfferingRamSizeField, serviceOfferingRamSizeField, "memory"); - return executeSqlCountComputingResourcesForAccount(accountId, sqlCountCpuNumberAllocatedToAccount); - } - private long executeSqlCountComputingResourcesForAccount(long accountId, String sqlCountComputingResourcesAllocatedToAccount) { TransactionLegacy tx = TransactionLegacy.currentTxn(); try { diff --git a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java index 434901ef5b3..a18097db6d6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/ClusterVO.java @@ -85,6 +85,10 @@ public class ClusterVO implements Cluster { @Column(name = "uuid") String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + + public ClusterVO() { clusterType = Cluster.ClusterType.CloudManaged; allocationState = Grouping.AllocationState.Enabled; @@ -215,6 +219,14 @@ public class ClusterVO implements Cluster { this.arch = arch; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("Cluster {id: \"%s\", name: \"%s\", uuid: \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java index 827b72b58b0..9b24e51a1a8 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java @@ -142,6 +142,9 @@ public class DataCenterVO implements DataCenter { @Enumerated(value = EnumType.STRING) private DataCenter.Type type; + @Column(name = "storage_access_groups") + String storageAccessGroups; + @Override public String getDnsProvider() { return dnsProvider; @@ -485,6 +488,14 @@ public class DataCenterVO implements DataCenter { this.type = type; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("Zone {\"id\": \"%s\", \"name\": \"%s\", \"uuid\": \"%s\"}", id, name, uuid); diff --git a/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java b/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java index fdda38fbc39..99ebcf2346c 100644 --- a/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/HostPodVO.java @@ -71,6 +71,9 @@ public class HostPodVO implements Pod { @Column(name = "uuid") private String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + public HostPodVO(String name, long dcId, String gateway, String cidrAddress, int cidrSize, String description) { this.name = name; this.dataCenterId = dcId; @@ -199,6 +202,14 @@ public class HostPodVO implements Pod { this.uuid = uuid; } + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + @Override public String toString() { return String.format("HostPod %s", diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java index bf12abd5114..6cfd2608f5d 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDao.java @@ -18,11 +18,11 @@ package com.cloud.dc.dao; import java.util.List; import java.util.Map; -import java.util.Set; import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface ClusterDao extends GenericDao { @@ -30,13 +30,11 @@ public interface ClusterDao extends GenericDao { ClusterVO findBy(String name, long podId); - List listByHyTypeWithoutGuid(String hyType); - List listByZoneId(long zoneId); List getAvailableHypervisorInZone(Long zoneId); - Set getDistinctAvailableHypervisorsAcrossClusters(); + List> listDistinctHypervisorsAndArchExcludingExternalType(Long zoneId); List listByDcHyType(long dcId, String hyType); @@ -59,4 +57,8 @@ public interface ClusterDao extends GenericDao { List getClustersArchsByZone(long zoneId); List listClustersByArchAndZoneId(long zoneId, CPU.CPUArch arch); + + List listDistinctStorageAccessGroups(String name, String keyword); + + List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java index af6b8397643..c63af0a237b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/ClusterDaoImpl.java @@ -21,10 +21,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; @@ -39,6 +37,7 @@ import com.cloud.dc.HostPodVO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; import com.cloud.org.Managed; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; @@ -148,14 +147,6 @@ public class ClusterDaoImpl extends GenericDaoBase implements C return findOneBy(sc); } - @Override - public List listByHyTypeWithoutGuid(String hyType) { - SearchCriteria sc = HyTypeWithoutGuidSearch.create(); - sc.setParameters("hypervisorType", hyType); - - return listBy(sc); - } - @Override public List listByDcHyType(long dcId, String hyType) { SearchCriteria sc = ZoneHyTypeSearch.create(); @@ -177,9 +168,30 @@ public class ClusterDaoImpl extends GenericDaoBase implements C .collect(Collectors.toList()); } + /** + * Returns distinct (HypervisorType, CPUArch) pairs from clusters in the given zone, + * excluding clusters with {@link HypervisorType#External}. + * + * @param zoneId the zone ID to filter by, or {@code null} to include all zones + * @return list of unique hypervisor type and CPU architecture pairs + */ @Override - public Set getDistinctAvailableHypervisorsAcrossClusters() { - return new HashSet<>(getAvailableHypervisorInZone(null)); + public List> listDistinctHypervisorsAndArchExcludingExternalType(Long zoneId) { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch()); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("hypervisorType", sb.entity().getHypervisorType(), SearchCriteria.Op.NEQ); + sb.done(); + SearchCriteria sc = sb.create(); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + sc.setParameters("hypervisorType", HypervisorType.External); + + final List clusters = search(sc, null); + return clusters.stream() + .map(c -> new Pair<>(c.getHypervisorType(), c.getArch())) + .collect(Collectors.toList()); } @Override @@ -344,4 +356,61 @@ public class ClusterDaoImpl extends GenericDaoBase implements C sc.setParameters("arch", arch); return listBy(sc); } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + + @Override + public List listEnabledClusterIdsByZoneHypervisorArch(Long zoneId, HypervisorType hypervisorType, CPU.CPUArch arch) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.and("allocationState", sb.entity().getAllocationState(), Op.EQ); + sb.and("managedState", sb.entity().getManagedState(), Op.EQ); + sb.and("hypervisor", sb.entity().getHypervisorType(), Op.EQ); + sb.and("arch", sb.entity().getArch(), Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("allocationState", Grouping.AllocationState.Enabled); + sc.setParameters("managedState", Managed.ManagedState.Managed); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + if (hypervisorType != null) { + sc.setParameters("hypervisor", hypervisorType); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java index dddbce31772..0ba88f39b23 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDao.java @@ -117,4 +117,6 @@ public interface DataCenterDao extends GenericDao { List listAllZones(); List listByIds(List ids); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java index 7719e5adfc7..d8ab12e82e6 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/DataCenterDaoImpl.java @@ -25,6 +25,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.db.GenericSearchBuilder; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -441,4 +442,36 @@ public class DataCenterDaoImpl extends GenericDaoBase implem sc.setParameters("ids", ids.toArray()); return listBy(sc); } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), SearchCriteria.Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java index b2e9b898606..2549a0555e8 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDao.java @@ -34,4 +34,6 @@ public interface HostPodDao extends GenericDao { public List listAllPods(Long zoneId); public List listAllPodsByCidr(long zoneId, String cidr); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java index f1835067380..08901c9a61e 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/HostPodDaoImpl.java @@ -143,4 +143,36 @@ public class HostPodDaoImpl extends GenericDaoBase implements H return listBy(sc); } + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java index 84f38f05441..a6c267bb189 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDao.java @@ -64,4 +64,6 @@ public interface VlanDao extends GenericDao { List listIpv6RangeByZoneIdAndVlanId(long zoneId, String vlanId); List listIpv6SupportingVlansByZone(long zoneId); + + List listVlansForExternalNetworkProvider(long zoneId, String detailKey); } diff --git a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java index 461a9a13b10..d9fad3cad12 100644 --- a/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/dc/dao/VlanDaoImpl.java @@ -26,6 +26,7 @@ import java.util.Map; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; @@ -67,6 +68,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected SearchBuilder ZoneVlanIp6Search; protected SearchBuilder ZoneIp6Search; protected SearchBuilder ZoneVlansSearch; + protected SearchBuilder ProviderVlanSearch; + protected SearchBuilder VlanDetailsProviderSearch; protected SearchBuilder AccountVlanMapSearch; protected SearchBuilder DomainVlanMapSearch; @@ -79,6 +82,8 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao protected DomainVlanMapDao _domainVlanMapDao; @Inject protected IPAddressDao _ipAddressDao; + @Inject + protected VlanDetailsDao vlanDetailsDao; @Override public VlanVO findByZoneAndVlanId(long zoneId, String vlanId) { @@ -277,6 +282,19 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao ZoneVlansSearch.and("zoneId", ZoneVlansSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); ZoneVlansSearch.and("vlan", ZoneVlansSearch.entity().getVlanTag(), SearchCriteria.Op.IN); ZoneVlansSearch.done(); + + ProviderVlanSearch = createSearchBuilder(); + ProviderVlanSearch.and("removed", ProviderVlanSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + ProviderVlanSearch.and("dataCenterId", ProviderVlanSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch = vlanDetailsDao.createSearchBuilder(); + VlanDetailsProviderSearch.and("name", VlanDetailsProviderSearch.entity().getName(), SearchCriteria.Op.EQ); + VlanDetailsProviderSearch.and("value", VlanDetailsProviderSearch.entity().getValue(), SearchCriteria.Op.EQ); + ProviderVlanSearch.join("VlanDetailsProviderSearch", VlanDetailsProviderSearch, ProviderVlanSearch.entity().getId(), + VlanDetailsProviderSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); + + VlanDetailsProviderSearch.done(); + ProviderVlanSearch.done(); + return result; } @@ -434,4 +452,13 @@ public class VlanDaoImpl extends GenericDaoBase implements VlanDao return listBy(sc); } + @Override + public List listVlansForExternalNetworkProvider(long zoneId, String detailKey) { + SearchCriteria sc = ProviderVlanSearch.create(); + sc.setParameters("dataCenterId", zoneId); + sc.setJoinParameters("VlanDetailsProviderSearch", "name", detailKey); + sc.setJoinParameters("VlanDetailsProviderSearch", "value", "true"); + return search(sc, null); + } + } diff --git a/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java new file mode 100644 index 00000000000..2410077c84a --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuCardVO.java @@ -0,0 +1,147 @@ +// 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.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.GpuCard; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gpu_card") +public class GpuCardVO implements GpuCard { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "device_id") + private String deviceId; + + @Column(name = "device_name") + private String deviceName; + + @Column(name = "name") + private String name; + + @Column(name = "vendor_name") + private String vendorName; + + @Column(name = "vendor_id") + private String vendorId; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public GpuCardVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuCardVO(String deviceId, String deviceName, String name, String vendorName, String vendorId) { + this.uuid = UUID.randomUUID().toString(); + this.deviceId = deviceId; + this.deviceName = deviceName; + this.name = name; + this.vendorName = vendorName; + this.vendorId = vendorId; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("GPUCard %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "deviceId", "deviceName", "vendorId", "vendorName")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public String getDeviceName() { + return deviceName; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getVendorName() { + return vendorName; + } + + public void setVendorName(String vendorName) { + this.vendorName = vendorName; + } + + @Override + public String getVendorId() { + return vendorId; + } + + public void setVendorId(String vendorId) { + this.vendorId = vendorId; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public String getGroupName() { + return "Group of " + getVendorName() + " " + getDeviceName() + " GPUs"; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java new file mode 100644 index 00000000000..ac20e74c360 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/GpuDeviceVO.java @@ -0,0 +1,200 @@ +// 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.gpu; + +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name = "gpu_device") +public class GpuDeviceVO implements GpuDevice { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "card_id") + private long cardId; + + @Column(name = "vgpu_profile_id") + private long vgpuProfileId; + + @Column(name = "bus_address") + private String busAddress; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "vm_id") + private Long vmId; + + @Column(name = "type") + @Enumerated(value = EnumType.STRING) + private DeviceType type = DeviceType.PCI; + + @Column(name = "state") + @Enumerated(value = EnumType.STRING) + private State state = State.Free; + + @Column(name = "managed_state") + @Enumerated(value = EnumType.STRING) + private ManagedState managedState = ManagedState.Managed; + + @Column(name = "parent_gpu_device_id") + private Long parentGpuDeviceId; + + @Column(name = "numa_node") + private String numaNode; + + @Column(name = "pci_root") + private String pciRoot; + + public GpuDeviceVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public GpuDeviceVO(long cardId, long vgpuProfileId, String busAddress, long hostId, Long parentGpuDeviceId, + String numaNode, String pciRoot) { + this.uuid = UUID.randomUUID().toString(); + this.cardId = cardId; + this.vgpuProfileId = vgpuProfileId; + this.busAddress = busAddress; + this.hostId = hostId; + this.parentGpuDeviceId = parentGpuDeviceId; + this.numaNode = numaNode; + this.pciRoot = pciRoot; + } + + @Override + public String toString() { + return String.format("GpuDevice %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "cardId", "vgpuProfileId", "busAddress", "hostId", "vmId", + "parentGpuDeviceId", "numaNode", "pciRoot", "state", "resourceState")); + } + + @Override + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public long getCardId() { + return cardId; + } + + public void setCardId(long cardId) { + this.cardId = cardId; + } + + public long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + public String getBusAddress() { + return busAddress; + } + + public void setBusAddress(String busAddress) { + this.busAddress = busAddress; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public DeviceType getType() { + return type; + } + + public void setType(DeviceType type) { + this.type = type; + } + + public ManagedState getManagedState() { + return managedState; + } + + public void setManagedState(ManagedState managedState) { + this.managedState = managedState; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public Long getParentGpuDeviceId() { + return parentGpuDeviceId; + } + + public void setParentGpuDeviceId(Long parentGpuDeviceId) { + this.parentGpuDeviceId = parentGpuDeviceId; + } + + public String getNumaNode() { + return numaNode; + } + + public void setNumaNode(String numaNode) { + this.numaNode = numaNode; + } + + public String getPciRoot() { + return pciRoot; + } + + public void setPciRoot(String pciRoot) { + this.pciRoot = pciRoot; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java index 5bbf90854ee..4944d51f1b4 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java +++ b/engine/schema/src/main/java/com/cloud/gpu/VGPUTypesVO.java @@ -40,19 +40,19 @@ public class VGPUTypesVO implements InternalIdentity { private String vgpuType; @Column(name="video_ram") - private long videoRam; + private Long videoRam; @Column(name="max_heads") - private long maxHeads; + private Long maxHeads; @Column(name="max_resolution_x") - private long maxResolutionX; + private Long maxResolutionX; @Column(name="max_resolution_y") - private long maxResolutionY; + private Long maxResolutionY; @Column(name="max_vgpu_per_pgpu") - private long maxVgpuPerPgpu; + private Long maxVgpuPerPgpu; @Column(name="remaining_capacity") private long remainingCapacity; @@ -63,7 +63,7 @@ public class VGPUTypesVO implements InternalIdentity { protected VGPUTypesVO() { } - public VGPUTypesVO(long gpuGroupId, String vgpuType, long videoRam, long maxHeads, long maxResolutionX, long maxResolutionY, long maxVgpuPerPgpu, + public VGPUTypesVO(long gpuGroupId, String vgpuType, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY, Long maxVgpuPerPgpu, long remainingCapacity, long maxCapacity) { this.gpuGroupId = gpuGroupId; this.vgpuType = vgpuType; @@ -92,43 +92,43 @@ public class VGPUTypesVO implements InternalIdentity { this.vgpuType = vgpuType; } - public long getVideoRam() { + public Long getVideoRam() { return videoRam; } - public void setVideoRam(long videoRam) { + public void setVideoRam(Long videoRam) { this.videoRam = videoRam; } - public long getMaxHeads() { + public Long getMaxHeads() { return maxHeads; } - public void setMaxHeads(long maxHeads) { + public void setMaxHeads(Long maxHeads) { this.maxHeads = maxHeads; } - public long getMaxResolutionX() { + public Long getMaxResolutionX() { return maxResolutionX; } - public void setMaxResolutionX(long maxResolutionX) { + public void setMaxResolutionX(Long maxResolutionX) { this.maxResolutionX = maxResolutionX; } - public long getMaxResolutionY() { + public Long getMaxResolutionY() { return maxResolutionY; } - public void setMaxResolutionY(long maxResolutionY) { + public void setMaxResolutionY(Long maxResolutionY) { this.maxResolutionY = maxResolutionY; } - public long getMaxVgpuPerPgpu() { + public Long getMaxVgpuPerPgpu() { return maxVgpuPerPgpu; } - public void setMaxVgpuPerPgpu(long maxVgpuPerPgpu) { + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { this.maxVgpuPerPgpu = maxVgpuPerPgpu; } diff --git a/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java new file mode 100644 index 00000000000..86f5eb94415 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/VgpuProfileVO.java @@ -0,0 +1,191 @@ +// 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.gpu; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gpu.VgpuProfile; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "vgpu_profile") +public class VgpuProfileVO implements VgpuProfile { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + + @Column(name = "card_id") + private Long cardId; + + @Column(name = "max_vgpu_per_pgpu") + private Long maxVgpuPerPgpu; + + @Column(name = "video_ram") + private Long videoRam; + + @Column(name = "max_heads") + private Long maxHeads; + + @Column(name = "max_resolution_x") + private Long maxResolutionX; + + @Column(name = "max_resolution_y") + private Long maxResolutionY; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + public VgpuProfileVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.created = new Date(); + } + + + public VgpuProfileVO(String name, String description, Long gpuCardId, Long maxVgpuPerPgpu, Long videoRam, Long maxHeads, Long maxResolutionX, Long maxResolutionY) { + this.uuid = UUID.randomUUID().toString(); + this.name = name; + this.description = description; + this.cardId = gpuCardId; + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + this.videoRam = videoRam; + this.maxHeads = maxHeads; + this.maxResolutionX = maxResolutionX; + this.maxResolutionY = maxResolutionY; + this.created = new Date(); + } + + @Override + public String toString() { + return String.format("VgpuProfile %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( + this, "id", "uuid", "name", "cardId")); + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Long getCardId() { + return cardId; + } + + public void setCardId(Long cardId) { + this.cardId = cardId; + } + + @Override + public Long getMaxVgpuPerPgpu() { + return maxVgpuPerPgpu; + } + + public void setMaxVgpuPerPgpu(Long maxVgpuPerPgpu) { + this.maxVgpuPerPgpu = maxVgpuPerPgpu; + } + + @Override + public Long getVideoRam() { + return videoRam; + } + + public void setVideoRam(Long videoRam) { + this.videoRam = videoRam; + } + + @Override + public Long getMaxHeads() { + return maxHeads; + } + + public void setMaxHeads(Long maxHeads) { + this.maxHeads = maxHeads; + } + + @Override + public Long getMaxResolutionX() { + return maxResolutionX; + } + + public void setMaxResolutionX(Long maxResolutionX) { + this.maxResolutionX = maxResolutionX; + } + + @Override + public Long getMaxResolutionY() { + return maxResolutionY; + } + + public void setMaxResolutionY(Long maxResolutionY) { + this.maxResolutionY = maxResolutionY; + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java new file mode 100644 index 00000000000..4463a690ae9 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDao.java @@ -0,0 +1,39 @@ +// 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.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuCardDao extends GenericDao { + + /** + * Find GPU card by vendor and device id + * + * @param vendorId the vendor id + * @param deviceId the device id + * @return GpuCardVO + */ + GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId); + + Pair, Integer> searchAndCountGpuCards( + Long id, String keyword, String vendorId, String vendorName, + String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java new file mode 100644 index 00000000000..8aad85d4508 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuCardDaoImpl.java @@ -0,0 +1,122 @@ +// 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.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class GpuCardDaoImpl extends GenericDaoBase implements GpuCardDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public GpuCardDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorId", allFieldSearch.entity().getVendorId(), SearchCriteria.Op.EQ); + allFieldSearch.and("vendorName", allFieldSearch.entity().getVendorName(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceId", allFieldSearch.entity().getDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and("deviceName", allFieldSearch.entity().getDeviceName(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + } + + @Override + public GpuCardVO findByVendorIdAndDeviceId(String vendorId, String deviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("vendorId", vendorId); + sc.setParameters("deviceId", deviceId); + return findOneBy(sc); + } + + @Override + public Pair, Integer> searchAndCountGpuCards(Long id, String keyword, String vendorId, + String vendorName, String deviceId, String deviceName, boolean activeOnly, Long startIndex, Long pageSize + ) { + + Filter searchFilter = new Filter(GpuCardVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.op("nameKeyword", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("deviceNameKeyword", sb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + sb.and("vendorNameKeyword", sb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.cp(); + } + if (vendorId != null) { + sb.and("vendorId", sb.entity().getVendorId(), SearchCriteria.Op.EQ); + } + if (vendorName != null) { + sb.and("vendorName", sb.entity().getVendorName(), SearchCriteria.Op.EQ); + } + if (deviceId != null) { + sb.and("deviceId", sb.entity().getDeviceId(), SearchCriteria.Op.EQ); + } + if (deviceName != null) { + sb.and("deviceName", sb.entity().getDeviceName(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (keyword != null) { + sc.setParameters("nameKeyword", "%" + keyword + "%"); + sc.setParameters("deviceNameKeyword", "%" + keyword + "%"); + sc.setParameters("vendorNameKeyword", "%" + keyword + "%"); + } + if (vendorId != null) { + sc.setParameters("vendorId", vendorId); + } + if (vendorName != null) { + sc.setParameters("vendorName", vendorName); + } + if (deviceId != null) { + sc.setParameters("deviceId", deviceId); + } + if (deviceName != null) { + sc.setParameters("deviceName", deviceName); + } + if (activeOnly) { + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + if (cardIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", cardIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java new file mode 100644 index 00000000000..e362f23888d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDao.java @@ -0,0 +1,71 @@ +// 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.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface GpuDeviceDao extends GenericDao { + + List listByIds(List ids); + + /** + * Find GPU device by host ID and bus address + * + * @param hostId the host ID + * @param busAddress the PCI bus address + * @return GpuDeviceVO + */ + GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress); + + /** + * List GPU devices by host ID + * + * @param hostId the ID of the host + * @return a list of GPU devices for the host + */ + List listByHostId(long hostId); + + /** + * List GPU devices by VM ID + * + * @param vmId the VM ID + * @return list of GpuDeviceVO + */ + List listByVmId(long vmId); + + boolean isVgpuProfileInUse(long vgpuProfileId); + + boolean isGpuCardInUse(long cardId); + + List listByHostAndVm(Long hostId, long vmId); + + List listDevicesForAllocation(Long hostId, Long vgpuProfileId); + + Pair, Integer> searchAndCountGpuDevices( + Long id, String keyword, Long hostId, Long vmId, Long gpuCardId, Long vgpuProfileId, + Long startIndex, Long pageSize); + + List getDistinctGpuCardIds(); + + List getDistinctVgpuProfileIds(); + + List listByParentGpuDeviceId(Long parentGpuDeviceId); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java new file mode 100644 index 00000000000..bd7032aff27 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/GpuDeviceDaoImpl.java @@ -0,0 +1,260 @@ +// 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.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gpu.GpuDevice; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class GpuDeviceDaoImpl extends GenericDaoBase implements GpuDeviceDao { + + private static final String IDS = "ids"; + private static final String HOST_ID = "hostId"; + private static final String VM_ID = "vmId"; + private static final String BUS_ADDRESS = "busAddress"; + private static final String CARD_ID = "cardId"; + private static final String VGPU_PROFILE_ID = "vgpuProfileId"; + private static final String PARENT_GPU_DEVICE_ID = "parentGpuDeviceId"; + private static final String STATE = "state"; + private static final String MANAGED_STATE = "managedState"; + private static final String TYPE = "type"; + private final SearchBuilder allFieldSearch; + private SearchBuilder devicesForAllocationSearch; + @Inject + private GpuCardDao gpuCardDao; + @Inject + private VgpuProfileDao vgpuProfileDao; + + public GpuDeviceDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and(IDS, allFieldSearch.entity().getId(), SearchCriteria.Op.IN); + allFieldSearch.and(HOST_ID, allFieldSearch.entity().getHostId(), SearchCriteria.Op.EQ); + allFieldSearch.and(CARD_ID, allFieldSearch.entity().getCardId(), SearchCriteria.Op.EQ); + allFieldSearch.and(BUS_ADDRESS, allFieldSearch.entity().getBusAddress(), SearchCriteria.Op.EQ); + allFieldSearch.and(STATE, allFieldSearch.entity().getState(), SearchCriteria.Op.EQ); + allFieldSearch.and(VGPU_PROFILE_ID, allFieldSearch.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + allFieldSearch.and(PARENT_GPU_DEVICE_ID, allFieldSearch.entity().getParentGpuDeviceId(), SearchCriteria.Op.EQ); + allFieldSearch.and(VM_ID, allFieldSearch.entity().getVmId(), SearchCriteria.Op.EQ); + allFieldSearch.done(); + + devicesForAllocationSearch = createSearchBuilder(); + devicesForAllocationSearch.and(HOST_ID, devicesForAllocationSearch.entity().getHostId(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(VGPU_PROFILE_ID, devicesForAllocationSearch.entity().getVgpuProfileId(), SearchCriteria.Op.IN); + devicesForAllocationSearch.and(STATE, devicesForAllocationSearch.entity().getState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(MANAGED_STATE, devicesForAllocationSearch.entity().getManagedState(), SearchCriteria.Op.EQ); + devicesForAllocationSearch.and(TYPE, devicesForAllocationSearch.entity().getType(), SearchCriteria.Op.NEQ); + devicesForAllocationSearch.done(); + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + return super.configure(name, params); + } + + @Override + public List listByIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(IDS, ids.toArray()); + return listBy(sc); + } + + @Override + public GpuDeviceVO findByHostIdAndBusAddress(long hostId, String busAddress) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(BUS_ADDRESS, busAddress); + return findOneBy(sc); + } + + @Override + public List listByHostId(long hostId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + return listBy(sc); + } + + @Override + public List listByVmId(long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VM_ID, vmId); + return listBy(sc); + } + + @Override + public boolean isVgpuProfileInUse(long vgpuProfileId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + return getCount(sc) > 0; + } + + @Override + public boolean isGpuCardInUse(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(CARD_ID, cardId); + return getCount(sc) > 0; + } + + @Override + public List listByHostAndVm(Long hostId, long vmId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VM_ID, vmId); + return search(sc, null); + } + + @Override + public List listDevicesForAllocation(Long hostId, Long vgpuProfileId) { + SearchCriteria sc = devicesForAllocationSearch.create(); + sc.setParameters(HOST_ID, hostId); + sc.setParameters(VGPU_PROFILE_ID, vgpuProfileId); + sc.setParameters(STATE, GpuDevice.State.Free); + sc.setParameters(MANAGED_STATE, GpuDevice.ManagedState.Managed); + sc.setParameters(TYPE, GpuDevice.DeviceType.VGPUOnly); + return search(sc, null); + } + + @Override + public Pair, Integer> searchAndCountGpuDevices(Long id, String keyword, Long hostId, Long vmId, + Long gpuCardId, Long vgpuProfileId, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(GpuDeviceVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (hostId != null) { + sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); + } + if (vmId != null) { + sb.and("vmId", sb.entity().getVmId(), SearchCriteria.Op.EQ); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (vgpuProfileId != null) { + sb.and("vgpuProfileId", sb.entity().getVgpuProfileId(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + SearchBuilder cardSb = gpuCardDao.createSearchBuilder(); + SearchBuilder profileSb = vgpuProfileDao.createSearchBuilder(); + sb.join("cardJoin", cardSb, sb.entity().getCardId(), cardSb.entity().getId(), JoinBuilder.JoinType.INNER); + sb.join("profileJoin", profileSb, sb.entity().getCardId(), profileSb.entity().getId(), + JoinBuilder.JoinType.INNER); + + sb.op("cardNameKeyword", cardSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getVendorName(), SearchCriteria.Op.LIKE); + sb.or("cardNameKeyword", cardSb.entity().getDeviceName(), SearchCriteria.Op.LIKE); + + sb.op("profileNameKeyword", profileSb.entity().getName(), SearchCriteria.Op.LIKE); + sb.op("profileDescriptionKeyword", profileSb.entity().getDescription(), SearchCriteria.Op.LIKE); + sb.cp(); + } + + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (hostId != null) { + sc.setParameters("hostId", hostId); + } + if (vmId != null) { + sc.setParameters("vmId", vmId); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + if (vgpuProfileId != null) { + sc.setParameters("vgpuProfileId", vgpuProfileId); + } + + if (keyword != null) { + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("cardJoin", "cardNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileNameKeyword", "%" + keyword + "%"); + sc.setJoinParameters("profileJoin", "profileDescriptionKeyword", "%" + keyword + "%"); + } + + return searchAndCount(sc, searchFilter); + } + + @Override + public List getDistinctGpuCardIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getCardId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getCardId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List getDistinctVgpuProfileIds() { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getVgpuProfileId()); + sb.done(); + SearchCriteria sc = sb.create(); + + List gpuDevices = listBy(sc); + if (CollectionUtils.isEmpty(gpuDevices)) { + return Collections.emptyList(); + } + + return gpuDevices.stream() + .map(GpuDeviceVO::getVgpuProfileId) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List listByParentGpuDeviceId(Long parentGpuDeviceId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters(PARENT_GPU_DEVICE_ID, parentGpuDeviceId); + return listBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java index 8e4f2f742ac..99e33617539 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDao.java @@ -19,6 +19,7 @@ package com.cloud.gpu.dao; import java.util.List; import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface HostGpuGroupsDao extends GenericDao { @@ -57,4 +58,15 @@ public interface HostGpuGroupsDao extends GenericDao { */ void persist(long hostId, List gpuGroups); + + /** + * Returns max and remaining GPU capacity + * + * @param dcId + * @param podId + * @param clusterId + * @param hostId + * @return Pair containing max GPU capacity and remaining GPU capacity + */ + Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId); } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java index 30535c7e27d..343b144597c 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java @@ -16,9 +16,16 @@ // under the License. package com.cloud.gpu.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; +import com.cloud.utils.Pair; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; import org.springframework.stereotype.Component; import com.cloud.gpu.HostGpuGroupsVO; @@ -87,4 +94,75 @@ public class HostGpuGroupsDaoImpl extends GenericDaoBase sc.setParameters("hostId", hostId); remove(sc); } + + @Override + public Pair getGpuStats(Long dcId, Long podId, Long clusterId, Long hostId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + Pair result = null; + List resourceIdList = new ArrayList<>(); + String query = getStatsQuery(resourceIdList, dcId, podId, clusterId, hostId); + + try { + PreparedStatement pstmt = txn.prepareAutoCloseStatement(query); + for (int i = 0; i < resourceIdList.size(); i++) { + pstmt.setLong(1 + i, resourceIdList.get(i)); + } + + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result = new Pair<>(rs.getLong(1), rs.getLong(2)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("Error while fetching GPU stats: " + e.getMessage(), e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + query, e); + } + } + + private String getStatsQuery(List resourceIdList, Long dcId, Long podId, Long clusterId, Long hostId) { + StringBuilder query = new StringBuilder("SELECT SUM(max_capacity), SUM(remaining_capacity)" + + "FROM vgpu_types " + + "WHERE" + + " gpu_group_id IN (" + + " SELECT" + + " host_gpu_groups.id" + + " FROM" + + " host_gpu_groups" + + " INNER JOIN host ON host.id = host_gpu_groups.host_id "); + if (dcId != null) { + query.append("WHERE host.data_center_id = ? "); + resourceIdList.add(dcId); + } + + if (podId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.pod_id = ? "); + resourceIdList.add(podId); + } + if (clusterId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.cluster_id = ? "); + resourceIdList.add(clusterId); + } + if (hostId != null) { + if (resourceIdList.isEmpty()) { + query.append("WHERE "); + } else { + query.append("AND "); + } + query.append(" host.id = ? "); + resourceIdList.add(hostId); + } + query.append(" )"); + return query.toString(); + } } diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java index edc5e1f67c8..524feed2467 100644 --- a/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VGPUTypesDaoImpl.java @@ -16,6 +16,17 @@ //under the License. package com.cloud.gpu.dao; +import com.cloud.agent.api.VgpuTypesInfo; +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -25,42 +36,30 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; -import javax.inject.Inject; - -import org.springframework.stereotype.Component; - -import com.cloud.agent.api.VgpuTypesInfo; -import com.cloud.gpu.HostGpuGroupsVO; -import com.cloud.gpu.VGPUTypesVO; -import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; - @Component public class VGPUTypesDaoImpl extends GenericDaoBase implements VGPUTypesDao { - private final SearchBuilder _searchByGroupId; - private final SearchBuilder _searchByGroupIdVGPUType; - - @Inject protected HostGpuGroupsDao _hostGpuGroupsDao; + @Inject + protected HostGpuGroupsDao hostGpuGroupsDao; private static final String LIST_ZONE_POD_CLUSTER_WIDE_GPU_CAPACITIES = "SELECT host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu, SUM(remaining_capacity) AS remaining_capacity, SUM(max_capacity) AS total_capacity FROM" + - " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + - " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND host.data_center_id = ?"; + " `cloud`.`vgpu_types` INNER JOIN `cloud`.`host_gpu_groups` ON vgpu_types.gpu_group_id = host_gpu_groups.id INNER JOIN `cloud`.`host`" + + " ON host_gpu_groups.host_id = host.id WHERE host.type = 'Routing' AND vgpu_types.max_capacity > 0 AND host.data_center_id = ?"; + + private final SearchBuilder searchByGroupId; + private final SearchBuilder searchByGroupIdVGPUType; public VGPUTypesDaoImpl() { - _searchByGroupId = createSearchBuilder(); - _searchByGroupId.and("groupId", _searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupId.done(); + searchByGroupId = createSearchBuilder(); + searchByGroupId.and("groupId", searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupId.done(); - _searchByGroupIdVGPUType = createSearchBuilder(); - _searchByGroupIdVGPUType.and("groupId", _searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.and("vgpuType", _searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); - _searchByGroupIdVGPUType.done(); + searchByGroupIdVGPUType = createSearchBuilder(); + searchByGroupIdVGPUType.and("groupId", searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.and("vgpuType", searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); + searchByGroupIdVGPUType.done(); } @Override @@ -83,7 +82,7 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen finalQuery.append(" AND host.cluster_id = ?"); resourceIdList.add(clusterId); } - finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type"); + finalQuery.append(" GROUP BY host_gpu_groups.group_name, vgpu_type, max_vgpu_per_pgpu"); try { pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); @@ -106,14 +105,14 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen @Override public List listByGroupId(long groupId) { - SearchCriteria sc = _searchByGroupId.create(); + SearchCriteria sc = searchByGroupId.create(); sc.setParameters("groupId", groupId); return listBy(sc); } @Override public VGPUTypesVO findByGroupIdVGPUType(long groupId, String vgpuType) { - SearchCriteria sc = _searchByGroupIdVGPUType.create(); + SearchCriteria sc = searchByGroupIdVGPUType.create(); sc.setParameters("groupId", groupId); sc.setParameters("vgpuType", vgpuType); return findOneBy(sc); @@ -124,7 +123,7 @@ public class VGPUTypesDaoImpl extends GenericDaoBase implemen Iterator>> it1 = groupDetails.entrySet().iterator(); while (it1.hasNext()) { Entry> entry = it1.next(); - HostGpuGroupsVO gpuGroup = _hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); + HostGpuGroupsVO gpuGroup = hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); HashMap values = entry.getValue(); Iterator> it2 = values.entrySet().iterator(); while (it2.hasNext()) { diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java new file mode 100644 index 00000000000..2628f1851f2 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDao.java @@ -0,0 +1,33 @@ +// 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.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface VgpuProfileDao extends GenericDao { + + VgpuProfileVO findByNameAndCardId(String name, long cardId); + + int removeByCardId(long cardId); + + Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, Long gpuCardId, + boolean activeOnly, Long startIndex, Long pageSize); +} diff --git a/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java new file mode 100644 index 00000000000..11dd7edb30d --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/gpu/dao/VgpuProfileDaoImpl.java @@ -0,0 +1,110 @@ +// 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.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.List; + +@Component +public class VgpuProfileDaoImpl extends GenericDaoBase implements VgpuProfileDao { + + private final SearchBuilder allFieldSearch; + + @Inject + private GpuDeviceDao gpuDeviceDao; + + public VgpuProfileDaoImpl() { + allFieldSearch = createSearchBuilder(); + allFieldSearch.and("name", allFieldSearch.entity().getName(), SearchCriteria.Op.EQ); + allFieldSearch.and("cardId", allFieldSearch.entity().getCardId(), SearchCriteria.Op.IN); + allFieldSearch.done(); + } + + @Override + public VgpuProfileVO findByNameAndCardId(String name, long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("name", name); + sc.setParameters("cardId", cardId); + return findOneBy(sc); + } + + @Override + public int removeByCardId(long cardId) { + SearchCriteria sc = allFieldSearch.create(); + sc.setParameters("cardId", cardId); + return remove(sc); + } + + @Override + public Pair, Integer> searchAndCountVgpuProfiles(Long id, String name, String keyword, + Long gpuCardId, boolean activeOnly, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(VgpuProfileVO.class, "id", true, startIndex, pageSize); + SearchBuilder sb = createSearchBuilder(); + + if (id != null) { + sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + } + if (name != null) { + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + } + if (keyword != null) { + sb.and("keywordName", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.and("keywordDescription", sb.entity().getDescription(), SearchCriteria.Op.LIKE); + } + if (gpuCardId != null) { + sb.and("cardId", sb.entity().getCardId(), SearchCriteria.Op.EQ); + } + if (activeOnly) { + sb.and("ids", sb.entity().getId(), SearchCriteria.Op.IN); + } + sb.done(); + + // Build search criteria + SearchCriteria sc = sb.create(); + if (id != null) { + sc.setParameters("id", id); + } + if (name != null) { + sc.setParameters("name", name); + } + if (keyword != null) { + sc.setParameters("keywordName", "%" + keyword + "%"); + sc.setParameters("keywordDescription", "%" + keyword + "%"); + } + if (gpuCardId != null) { + sc.setParameters("cardId", gpuCardId); + } + + if (activeOnly) { + List vgpuProfileIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + if (vgpuProfileIds.isEmpty()) { + return new Pair<>(List.of(), 0); + } + sc.setParameters("ids", vgpuProfileIds.toArray()); + } + + return searchAndCount(sc, searchFilter); + } +} diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java b/engine/schema/src/main/java/com/cloud/host/HostVO.java index bd6768fa0dd..d51b4eca057 100644 --- a/engine/schema/src/main/java/com/cloud/host/HostVO.java +++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java @@ -165,6 +165,9 @@ public class HostVO implements Host { @Column(name = "uuid") private String uuid; + @Column(name = "storage_access_groups") + String storageAccessGroups; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call host dao to load it. @@ -357,6 +360,15 @@ public class HostVO implements Host { return isTagARule; } + @Override + public String getStorageAccessGroups() { + return storageAccessGroups; + } + + public void setStorageAccessGroups(String storageAccessGroups) { + this.storageAccessGroups = storageAccessGroups; + } + public HashMap> getGpuGroupDetails() { return groupDetails; } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java index cfd75b1a94b..090b019334f 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -19,6 +19,7 @@ package com.cloud.host.dao; import java.util.Date; import java.util.List; +import com.cloud.cpu.CPU; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostVO; @@ -30,6 +31,7 @@ import com.cloud.resource.ResourceState; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; import com.cloud.utils.fsm.StateDao; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; /** * Data Access Object for server @@ -82,6 +84,10 @@ public interface HostDao extends GenericDao, StateDao findHypervisorHostInCluster(long clusterId); + List findHypervisorHostInPod(long podId); + + List findHypervisorHostInZone(long zoneId); + HostVO findAnyStateHypervisorHostInCluster(long clusterId); HostVO findOldestExistentHypervisorHostInCluster(long clusterId); @@ -94,10 +100,14 @@ public interface HostDao extends GenericDao, StateDao findByPodId(Long podId); + List findByPodId(Long podId, Type type); + List listIdsByPodId(Long podId); List findByClusterId(Long clusterId); + List findByClusterId(Long clusterId, Type type); + List listIdsByClusterId(Long clusterId); List listIdsForUpRouting(Long zoneId, Long podId, Long clusterId); @@ -167,14 +177,24 @@ public interface HostDao extends GenericDao, StateDao listHostsByMsAndDc(long msId, long dcId); + List listHostsByMsDcResourceState(long msId, long dcId, List excludedResourceStates); + List listHostsByMs(long msId); + List listHostsByMsResourceState(long msId, List excludedResourceStates); + /** - * Retrieves the number of hosts/agents this {@see ManagementServer} has responsibility over. - * @param msId the id of the {@see ManagementServer} - * @return the number of hosts/agents this {@see ManagementServer} has responsibility over + * Count Hosts by given Management Server, Host and Hypervisor Types, + * and exclude Hosts with given Resource States. + * + * @param msId Management Server Id + * @param excludedResourceStates Resource States to be excluded + * @param hostTypes Host Types + * @param hypervisorTypes Hypervisor Types + * @return Hosts count */ - int countByMs(long msId); + int countHostsByMsResourceStateTypeAndHypervisorType(long msId, List excludedResourceStates, + List hostTypes, List hypervisorTypes); /** * Retrieves the host ids/agents this {@see ManagementServer} has responsibility over. @@ -183,6 +203,13 @@ public interface HostDao extends GenericDao, StateDao listByMs(long msId); + /** + * Retrieves the last host ids/agents this {@see ManagementServer} has responsibility over. + * @param msId the id of the {@see ManagementServer} + * @return the last host ids/agents this {@see ManagementServer} has responsibility over + */ + List listByLastMs(long msId); + /** * Retrieves the hypervisor versions of the hosts in the datacenter which are in Up state in ascending order * @param datacenterId data center id @@ -200,10 +227,18 @@ public interface HostDao extends GenericDao, StateDao findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(final Long zoneId, final Long clusterId, - final List resourceStates, final List types, + final Long msId, final List resourceStates, final List types, final List hypervisorTypes); List listDistinctHypervisorTypes(final Long zoneId); + List> listDistinctHypervisorArchTypes(final Long zoneId); + + List listDistinctArchTypes(final Long clusterId); + List listByIds(final List ids); + + Long findClusterIdByVolumeInfo(VolumeInfo volumeInfo); + + List listDistinctStorageAccessGroups(String name, String keyword); } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index 54146e55049..8f218841b07 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -35,6 +35,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.persistence.TableGenerator; +import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper; import org.apache.commons.collections.CollectionUtils; @@ -42,6 +44,7 @@ import com.cloud.agent.api.VgpuTypesInfo; import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.configuration.ManagementServiceConfiguration; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.gpu.dao.HostGpuGroupsDao; @@ -69,6 +72,7 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; @@ -76,6 +80,7 @@ import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.lang3.ObjectUtils; @DB @TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1) @@ -103,7 +108,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder IdStatusSearch; protected SearchBuilder TypeDcSearch; protected SearchBuilder TypeDcStatusSearch; - protected SearchBuilder TypeClusterStatusSearch; + protected SearchBuilder TypeStatusStateSearch; protected SearchBuilder MsStatusSearch; protected SearchBuilder DcPrivateIpAddressSearch; protected SearchBuilder DcStorageIpAddressSearch; @@ -129,6 +134,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao protected SearchBuilder ResponsibleMsSearch; protected SearchBuilder ResponsibleMsDcSearch; protected GenericSearchBuilder ResponsibleMsIdSearch; + protected GenericSearchBuilder LastMsIdSearch; protected SearchBuilder HostTypeClusterCountSearch; protected SearchBuilder HostTypeZoneCountSearch; protected SearchBuilder ClusterStatusSearch; @@ -209,6 +215,11 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao ResponsibleMsIdSearch.and("managementServerId", ResponsibleMsIdSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); ResponsibleMsIdSearch.done(); + LastMsIdSearch = createSearchBuilder(String.class); + LastMsIdSearch.selectFields(LastMsIdSearch.entity().getUuid()); + LastMsIdSearch.and("lastManagementServerId", LastMsIdSearch.entity().getLastManagementServerId(), SearchCriteria.Op.EQ); + LastMsIdSearch.done(); + HostTypeClusterCountSearch = createSearchBuilder(); HostTypeClusterCountSearch.and("cluster", HostTypeClusterCountSearch.entity().getClusterId(), SearchCriteria.Op.EQ); HostTypeClusterCountSearch.and("type", HostTypeClusterCountSearch.entity().getType(), SearchCriteria.Op.EQ); @@ -256,12 +267,14 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao TypeDcStatusSearch.and("resourceState", TypeDcStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); TypeDcStatusSearch.done(); - TypeClusterStatusSearch = createSearchBuilder(); - TypeClusterStatusSearch.and("type", TypeClusterStatusSearch.entity().getType(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("cluster", TypeClusterStatusSearch.entity().getClusterId(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("status", TypeClusterStatusSearch.entity().getStatus(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.and("resourceState", TypeClusterStatusSearch.entity().getResourceState(), SearchCriteria.Op.EQ); - TypeClusterStatusSearch.done(); + TypeStatusStateSearch = createSearchBuilder(); + TypeStatusStateSearch.and("type", TypeStatusStateSearch.entity().getType(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("cluster", TypeStatusStateSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("pod", TypeStatusStateSearch.entity().getPodId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("zone", TypeStatusStateSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("status", TypeStatusStateSearch.entity().getStatus(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.and("resourceState", TypeStatusStateSearch.entity().getResourceState(), SearchCriteria.Op.EQ); + TypeStatusStateSearch.done(); IdsSearch = createSearchBuilder(); IdsSearch.and("id", IdsSearch.entity().getId(), SearchCriteria.Op.IN); @@ -318,10 +331,12 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao PodSearch = createSearchBuilder(); PodSearch.and("podId", PodSearch.entity().getPodId(), SearchCriteria.Op.EQ); + PodSearch.and("type", PodSearch.entity().getType(), Op.EQ); PodSearch.done(); ClusterSearch = createSearchBuilder(); ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ); + ClusterSearch.and("type", ClusterSearch.entity().getType(), Op.EQ); ClusterSearch.done(); TypeSearch = createSearchBuilder(); @@ -1228,8 +1243,16 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List findByPodId(Long podId) { + return findByPodId(podId, null); + } + + @Override + public List findByPodId(Long podId, Type type) { SearchCriteria sc = PodSearch.create(); sc.setParameters("podId", podId); + if (type != null) { + sc.setParameters("type", Type.Routing); + } return listBy(sc); } @@ -1240,8 +1263,16 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List findByClusterId(Long clusterId) { + return findByClusterId(clusterId, null); + } + + @Override + public List findByClusterId(Long clusterId, Type type) { SearchCriteria sc = ClusterSearch.create(); sc.setParameters("clusterId", clusterId); + if (type != null) { + sc.setParameters("type", Type.Routing); + } return listBy(sc); } @@ -1345,7 +1376,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List findHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); sc.setParameters("status", Status.Up); @@ -1354,9 +1385,31 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } + @Override + public List findHypervisorHostInZone(long zoneId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("zone", zoneId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + + @Override + public List findHypervisorHostInPod(long podId) { + SearchCriteria sc = TypeStatusStateSearch.create(); + sc.setParameters("type", Host.Type.Routing); + sc.setParameters("pod", podId); + sc.setParameters("status", Status.Up); + sc.setParameters("resourceState", ResourceState.Enabled); + + return listBy(sc); + } + @Override public HostVO findAnyStateHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); List list = listBy(sc, new Filter(1)); @@ -1365,7 +1418,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public HostVO findOldestExistentHypervisorHostInCluster(long clusterId) { - SearchCriteria sc = TypeClusterStatusSearch.create(); + SearchCriteria sc = TypeStatusStateSearch.create(); sc.setParameters("type", Host.Type.Routing); sc.setParameters("cluster", clusterId); sc.setParameters("status", Status.Up); @@ -1548,6 +1601,17 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return listBy(sc); } + @Override + public List listHostsByMsDcResourceState(long msId, long dcId, List excludedResourceStates) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + return listBy(sc.create()); + } + @Override public List listHostsByMs(long msId) { SearchCriteria sc = ResponsibleMsSearch.create(); @@ -1556,10 +1620,32 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } @Override - public int countByMs(long msId) { - SearchCriteria sc = ResponsibleMsSearch.create(); - sc.setParameters("managementServerId", msId); - return getCount(sc); + public List listHostsByMsResourceState(long msId, List excludedResourceStates) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + return listBy(sc.create()); + } + + @Override + public int countHostsByMsResourceStateTypeAndHypervisorType(long msId, + List excludedResourceStates, + List hostTypes, + List hypervisorTypes) { + QueryBuilder sc = QueryBuilder.create(HostVO.class); + sc.and(sc.entity().getManagementServerId(), Op.EQ, msId); + if (CollectionUtils.isNotEmpty(excludedResourceStates)) { + sc.and(sc.entity().getResourceState(), Op.NIN, excludedResourceStates.toArray()); + } + if (CollectionUtils.isNotEmpty(hostTypes)) { + sc.and(sc.entity().getType(), Op.IN, hostTypes.toArray()); + } + if (CollectionUtils.isNotEmpty(hypervisorTypes)) { + sc.and(sc.entity().getHypervisorType(), Op.IN, hypervisorTypes.toArray()); + } + return getCount(sc.create()); } @Override @@ -1569,6 +1655,13 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return customSearch(sc, null); } + @Override + public List listByLastMs(long msId) { + SearchCriteria sc = LastMsIdSearch.create(); + sc.addAnd("lastManagementServerId", SearchCriteria.Op.EQ, msId); + return customSearch(sc, null); + } + @Override public List listOrderedHostsHypervisorVersionsInDatacenter(long datacenterId, HypervisorType hypervisorType) { PreparedStatement pstmt; @@ -1745,13 +1838,15 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao } @Override - public List findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(final Long zoneId, final Long clusterId, + public List findHostIdsByZoneClusterResourceStateTypeAndHypervisorType(final Long zoneId, + final Long clusterId, final Long managementServerId, final List resourceStates, final List types, final List hypervisorTypes) { GenericSearchBuilder sb = createSearchBuilder(Long.class); sb.selectFields(sb.entity().getId()); sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("msId", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ); sb.and("resourceState", sb.entity().getResourceState(), SearchCriteria.Op.IN); sb.and("type", sb.entity().getType(), SearchCriteria.Op.IN); if (CollectionUtils.isNotEmpty(hypervisorTypes)) { @@ -1767,6 +1862,9 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao if (clusterId != null) { sc.setParameters("clusterId", clusterId); } + if (managementServerId != null) { + sc.setParameters("msId", managementServerId); + } if (CollectionUtils.isNotEmpty(hypervisorTypes)) { sc.setParameters("hypervisorTypes", hypervisorTypes.toArray()); } @@ -1777,17 +1875,52 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List listDistinctHypervisorTypes(final Long zoneId) { - GenericSearchBuilder sb = createSearchBuilder(HypervisorType.class); + GenericSearchBuilder sb = createSearchBuilder(String.class); sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); sb.select(null, Func.DISTINCT, sb.entity().getHypervisorType()); sb.done(); - SearchCriteria sc = sb.create(); + SearchCriteria sc = sb.create(); if (zoneId != null) { sc.setParameters("zoneId", zoneId); } sc.setParameters("type", Type.Routing); - return customSearch(sc, null); + List hypervisorString = customSearch(sc, null); + return hypervisorString.stream().map(HypervisorType::getType).collect(Collectors.toList()); + } + + @Override + public List> listDistinctHypervisorArchTypes(final Long zoneId) { + SearchBuilder sb = createSearchBuilder(); + sb.select(null, Func.DISTINCT_PAIR, sb.entity().getHypervisorType(), sb.entity().getArch()); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.and("zoneId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("type", Type.Routing); + if (zoneId != null) { + sc.setParameters("zoneId", zoneId); + } + final List hosts = search(sc, null); + return hosts.stream() + .map(h -> new Pair<>(h.getHypervisorType(), h.getArch())) + .collect(Collectors.toList()); + } + + @Override + public List listDistinctArchTypes(final Long clusterId) { + GenericSearchBuilder sb = createSearchBuilder(String.class); + sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); + sb.and("type", sb.entity().getType(), SearchCriteria.Op.EQ); + sb.select(null, Func.DISTINCT, sb.entity().getArch()); + sb.done(); + SearchCriteria sc = sb.create(); + if (clusterId != null) { + sc.setParameters("clusterId", clusterId); + } + sc.setParameters("type", Type.Routing); + List archStrings = customSearch(sc, null); + return archStrings.stream().map(CPU.CPUArch::fromType).collect(Collectors.toList()); } @Override @@ -1799,4 +1932,56 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao sc.setParameters("id", ids.toArray()); return search(sc, null); } + + + @Override + public Long findClusterIdByVolumeInfo(VolumeInfo volumeInfo) { + VirtualMachine virtualMachine = volumeInfo.getAttachedVM(); + if (virtualMachine == null) { + return null; + } + + Long hostId = ObjectUtils.defaultIfNull(virtualMachine.getHostId(), virtualMachine.getLastHostId()); + Host host = findById(hostId); + + if (host == null) { + logger.warn(String.format("VM [%s] has null host on DB, either this VM was never started, or there is some inconsistency on the DB.", virtualMachine.getUuid())); + return null; + } + + return host.getClusterId(); + } + + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroups()); + if (name != null) { + searchBuilder.and().op("storageAccessGroupExact", searchBuilder.entity().getStorageAccessGroups(), Op.EQ); + searchBuilder.or("storageAccessGroupPrefix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupSuffix", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.or("storageAccessGroupMiddle", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + searchBuilder.cp(); + } + if (keyword != null) { + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroups(), Op.LIKE); + } + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + if (name != null) { + sc.setParameters("storageAccessGroupExact", name); + sc.setParameters("storageAccessGroupPrefix", name + ",%"); + sc.setParameters("storageAccessGroupSuffix", "%," + name); + sc.setParameters("storageAccessGroupMiddle", "%," + name + ",%"); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java index 8dc4efa91f3..5d8bd0a0a3a 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java @@ -32,4 +32,7 @@ public interface HostDetailsDao extends GenericDao { void deleteDetails(long hostId); List findByName(String name); + + void replaceExternalDetails(long hostId, Map details); + } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java index 9c1340592f9..3eb9faeb1c1 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java @@ -18,6 +18,7 @@ package com.cloud.host.dao; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +32,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VmDetailConstants; @Component public class HostDetailsDaoImpl extends GenericDaoBase implements HostDetailsDao { @@ -130,4 +132,34 @@ public class HostDetailsDaoImpl extends GenericDaoBase implement sc.setParameters("name", name); return listBy(sc); } + + @Override + public void replaceExternalDetails(long hostId, Map details) { + if (details.isEmpty()) { + return; + } + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + List detailVOs = new ArrayList<>(); + for (Map.Entry entry : details.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + if ("password".equals(entry.getKey())) { + value = DBEncryptionUtil.encrypt(value); + } + detailVOs.add(new DetailVO(hostId, name, value)); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("name", VmDetailConstants.EXTERNAL_DETAIL_PREFIX + "%"); + remove(sc); + for (DetailVO detail : detailVOs) { + persist(detail); + } + txn.commit(); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java index 718511746c2..9775f8ad5b1 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java @@ -37,4 +37,6 @@ public interface AutoScaleVmGroupVmMapDao extends GenericDao vmIds, Long batchSize); + + int getErroredInstanceCount(long vmGroupId); } diff --git a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java index 1ae55d97da2..b2f4e578a82 100644 --- a/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java @@ -127,4 +127,13 @@ public class AutoScaleVmGroupVmMapDaoImpl extends GenericDaoBase sc = CountBy.create(); + sc.setParameters("vmGroupId", vmGroupId); + sc.setJoinParameters("vmSearch", "states", State.Error); + final List results = customSearch(sc, null); + return results.get(0); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java index 056445225d0..7f322ae6c03 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDao.java @@ -75,5 +75,7 @@ public interface FirewallRulesDao extends GenericDao { void loadDestinationCidrs(FirewallRuleVO rule); + FirewallRuleVO findByNetworkIdAndPorts(long networkId, int startPort, int endPort); + List listRoutingIngressFirewallRules(long networkId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java index a793a9172d4..27bf7ba6aa8 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -48,6 +48,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i protected final SearchBuilder NotRevokedSearch; protected final SearchBuilder ReleaseSearch; protected SearchBuilder VmSearch; + protected SearchBuilder FirewallByPortsAndNetwork; protected final SearchBuilder SystemRuleSearch; protected final GenericSearchBuilder RulesByIpCount; protected final SearchBuilder RoutingFirewallRulesSearch; @@ -106,6 +107,12 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i RulesByIpCount.and("state", RulesByIpCount.entity().getState(), Op.EQ); RulesByIpCount.done(); + FirewallByPortsAndNetwork = createSearchBuilder(); + FirewallByPortsAndNetwork.and("networkId", FirewallByPortsAndNetwork.entity().getNetworkId(), Op.EQ); + FirewallByPortsAndNetwork.and("sourcePortStart", FirewallByPortsAndNetwork.entity().getSourcePortStart(), Op.EQ); + FirewallByPortsAndNetwork.and("sourcePortEnd", FirewallByPortsAndNetwork.entity().getSourcePortEnd(), Op.EQ); + FirewallByPortsAndNetwork.done(); + RoutingFirewallRulesSearch = createSearchBuilder(); RoutingFirewallRulesSearch.and("networkId", RoutingFirewallRulesSearch.entity().getNetworkId(), Op.EQ); RoutingFirewallRulesSearch.and("purpose", RoutingFirewallRulesSearch.entity().getPurpose(), Op.EQ); @@ -408,6 +415,16 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i rule.setDestinationCidrsList(destCidrs); } + @Override + public FirewallRuleVO findByNetworkIdAndPorts(long networkId, int startPort, int endPort) { + SearchCriteria sc = FirewallByPortsAndNetwork.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + + return findOneBy(sc); + } + @Override public List listRoutingIngressFirewallRules(long networkId) { SearchCriteria sc = RoutingFirewallRulesSearch.create(); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java index 5499d04e3a1..0a5ecd25667 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressDaoImpl.java @@ -197,6 +197,7 @@ public class IPAddressDaoImpl extends GenericDaoBase implemen address.setSourceNat(false); address.setOneToOneNat(false); address.setAssociatedWithVmId(null); + address.setForRouter(false); address.setState(State.Free); address.setAssociatedWithNetworkId(null); address.setVpcId(null); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java index 88e146d2a80..a3a65fdb01b 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/IPAddressVO.java @@ -117,6 +117,9 @@ public class IPAddressVO implements IpAddress { @Column(name = "forsystemvms") private boolean forSystemVms = false; + @Column(name = "for_router") + private boolean forRouter = false; + @Column(name= GenericDao.REMOVED_COLUMN) private Date removed; @@ -388,4 +391,13 @@ public class IPAddressVO implements IpAddress { public boolean isForSystemVms() { return forSystemVms; } + + @Override + public boolean isForRouter() { + return forRouter; + } + + public void setForRouter(boolean forRouter) { + this.forRouter = forRouter; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVO.java b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVO.java index ad0338b9849..3886529322e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/LoadBalancerVO.java @@ -144,4 +144,13 @@ public class LoadBalancerVO extends FirewallRuleVO implements LoadBalancer { ReflectionToStringBuilderUtils.reflectOnlySelectedFields( this, "id", "uuid", "name", "purpose", "state")); } + + /** + * Sets the CIDR list associated with this load balancer rule. + * + * @param cidrList a comma-separated list of CIDR strings, e.g. "1.2.3.4/24,1.2.3.5/24" or an empty string e.g. "" to clear the restrictions + */ + public void setCidrList(String cidrList) { + this.cidrList = cidrList; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java new file mode 100644 index 00000000000..fe21f72e4db --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDao.java @@ -0,0 +1,24 @@ +// 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.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.GenericDao; + +public interface NetrisProviderDao extends GenericDao { + NetrisProviderVO findByZoneId(long zoneId); +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java new file mode 100644 index 00000000000..86ea04f1db0 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NetrisProviderDaoImpl.java @@ -0,0 +1,52 @@ +// 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.network.dao; + +import com.cloud.network.element.NetrisProviderVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +@Component +@DB() +public class NetrisProviderDaoImpl extends GenericDaoBase implements NetrisProviderDao { + + final SearchBuilder allFieldsSearch; + + public NetrisProviderDaoImpl() { + super(); + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("id", allFieldsSearch.entity().getId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("hostname", allFieldsSearch.entity().getUrl(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(), + SearchCriteria.Op.EQ); + allFieldsSearch.done(); + } + + @Override + public NetrisProviderVO findByZoneId(long zoneId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("zone_id", zoneId); + return findOneBy(sc); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java index 4811b59d31e..09d9f1d7fbf 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java @@ -125,7 +125,7 @@ public class PhysicalNetworkTrafficTypeDaoImpl extends GenericDaoBase List findByAccount(Long accountId); List listByNetworkId(Long networkId); + + List listByVpcId(Long vpcId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java index 484aa6f6631..ccbc60a5562 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/RemoteAccessVpnDaoImpl.java @@ -85,4 +85,11 @@ public class RemoteAccessVpnDaoImpl extends GenericDaoBase listByVpcId(Long vpcId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java index d3fef252f50..3475003c269 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDao.java @@ -20,4 +20,6 @@ import com.cloud.utils.db.GenericDao; public interface Site2SiteVpnGatewayDao extends GenericDao { Site2SiteVpnGatewayVO findByVpcId(long vpcId); + + Site2SiteVpnGatewayVO findByPublicIpAddress(long ipAddressId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java index d1fde963217..0aeefe90c29 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteVpnGatewayDaoImpl.java @@ -35,6 +35,7 @@ public class Site2SiteVpnGatewayDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("ipAddressId", ipAddressId); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java index 80bb44a1f4a..1e73cd7b33e 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDao.java @@ -22,4 +22,6 @@ import com.cloud.utils.db.GenericDao; public interface SslCertDao extends GenericDao { List listByAccountId(Long id); + + int removeByAccountId(long accountId); } diff --git a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java index 185c18aecd8..efadc009dfc 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/SslCertDaoImpl.java @@ -40,4 +40,10 @@ public class SslCertDaoImpl extends GenericDaoBase implements S return listBy(sc); } + @Override + public int removeByAccountId(long accountId) { + SearchCriteria sc = listByAccountId.create(); + sc.setParameters("accountId", accountId); + return remove(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java new file mode 100644 index 00000000000..113678f7b01 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/element/NetrisProviderVO.java @@ -0,0 +1,265 @@ +// 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.network.element; + +import com.cloud.network.netris.NetrisProvider; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "netris_providers") +public class NetrisProviderVO implements NetrisProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "url") + private String url; + + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; + + @Column(name = "site_name") + private String siteName; + + @Column(name = "tenant_name") + private String tenantName; + + @Column(name = "netris_tag") + private String netrisTag; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + + public NetrisProviderVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getSiteName() { + return siteName; + } + + public void setSiteName(String siteName) { + this.siteName = siteName; + } + + public String getTenantName() { + return tenantName; + } + + public void setTenantName(String tenantName) { + this.tenantName = tenantName; + } + + public String getNetrisTag() { + return netrisTag; + } + + public void setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public static final class Builder { + private long zoneId; + private long hostId; + private String name; + private String url; + private String username; + private String password; + private String siteName; + private String tenantName; + private String netrisTag; + + public Builder() { + // Default constructor + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setHostId(long hostId) { + this.hostId = hostId; + return this; + } + + public Builder setName(String name) { + this.name = name; + return this; + } + + public Builder setUrl(String url) { + this.url = url; + return this; + } + + public Builder setUsername(String username) { + this.username = username; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setSiteName(String siteName) { + this.siteName = siteName; + return this; + } + + public Builder setTenantName(String tenantName) { + this.tenantName = tenantName; + return this; + } + + public Builder setNetrisTag(String netrisTag) { + this.netrisTag = netrisTag; + return this; + } + + public NetrisProviderVO build() { + NetrisProviderVO provider = new NetrisProviderVO(); + provider.setZoneId(this.zoneId); + provider.setHostId(this.hostId); + provider.setUuid(UUID.randomUUID().toString()); + provider.setName(this.name); + provider.setUrl(this.url); + provider.setUsername(this.username); + provider.setPassword(this.password); + provider.setSiteName(this.siteName); + provider.setTenantName(this.tenantName); + provider.setNetrisTag(this.netrisTag); + provider.setCreated(new Date()); + return provider; + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java index 8cd114b7fc4..a737f1b9a20 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDao.java @@ -47,5 +47,7 @@ public interface PortForwardingRulesDao extends GenericDao listByNetworkAndDestIpAddr(String ip4Address, long networkId); + + PortForwardingRuleVO findByNetworkAndPorts(long networkId, int startPort, int endPort); int expungeByVmList(List vmIds, Long batchSize); } diff --git a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java index 1b3df06e1a2..637f47731b4 100644 --- a/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/rules/dao/PortForwardingRulesDaoImpl.java @@ -58,6 +58,8 @@ public class PortForwardingRulesDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("sourcePortStart", startPort); + sc.setParameters("sourcePortEnd", endPort); + return findOneBy(sc); + } + @Override public int expungeByVmList(List vmIds, Long batchSize) { if (CollectionUtils.isEmpty(vmIds)) { diff --git a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java index d12b9f9443f..59699cba1d4 100644 --- a/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java +++ b/engine/schema/src/main/java/com/cloud/network/security/SecurityGroupVMMapVO.java @@ -50,6 +50,9 @@ public class SecurityGroupVMMapVO implements InternalIdentity { @Column(name = "ip4_address", table = "nics", insertable = false, updatable = false) private String guestIpAddress; + @Column(name = "ip6_address", table = "nics", insertable = false, updatable = false) + private String guestIpv6Address; + @Column(name = "state", table = "vm_instance", insertable = false, updatable = false) private State vmState; @@ -77,6 +80,10 @@ public class SecurityGroupVMMapVO implements InternalIdentity { return guestIpAddress; } + public String getGuestIpv6Address() { + return guestIpv6Address; + } + public long getInstanceId() { return instanceId; } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java index 2246bd6eed2..632d96819cd 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/StaticRouteVO.java @@ -27,6 +27,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import com.cloud.utils.db.GenericDao; @@ -42,7 +43,10 @@ public class StaticRouteVO implements StaticRoute { String uuid; @Column(name = "vpc_gateway_id", updatable = false) - long vpcGatewayId; + Long vpcGatewayId; + + @Column(name = "next_hop") + private String nextHop; @Column(name = "cidr") private String cidr; @@ -67,6 +71,9 @@ public class StaticRouteVO implements StaticRoute { uuid = UUID.randomUUID().toString(); } + @Transient + boolean forVpn = false; + /** * @param vpcGatewayId * @param cidr @@ -74,7 +81,7 @@ public class StaticRouteVO implements StaticRoute { * @param accountId TODO * @param domainId TODO */ - public StaticRouteVO(long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId) { + public StaticRouteVO(Long vpcGatewayId, String cidr, Long vpcId, long accountId, long domainId, String nextHop) { super(); this.vpcGatewayId = vpcGatewayId; this.cidr = cidr; @@ -82,14 +89,32 @@ public class StaticRouteVO implements StaticRoute { this.vpcId = vpcId; this.accountId = accountId; this.domainId = domainId; + this.nextHop = nextHop; uuid = UUID.randomUUID().toString(); } + public StaticRouteVO(String cidr, Long vpcId, long accountId, long domainId, String nextHop, State state, boolean forVpn) { + super(); + this.cidr = cidr; + this.state = state; + this.vpcId = vpcId; + this.accountId = accountId; + this.domainId = domainId; + this.nextHop = nextHop; + uuid = UUID.randomUUID().toString(); + this.forVpn = forVpn; + } + @Override - public long getVpcGatewayId() { + public Long getVpcGatewayId() { return vpcGatewayId; } + @Override + public String getNextHop() { + return nextHop; + } + @Override public String getCidr() { return cidr; @@ -145,4 +170,8 @@ public class StaticRouteVO implements StaticRoute { public String getName() { return null; } + + public boolean isForVpn() { + return forVpn; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java index 274b9fedecc..9320a37bc96 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java @@ -60,9 +60,6 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "default") boolean isDefault = false; - @Column(name = "for_nsx") - boolean forNsx = false; - @Column(name = "network_mode") NetworkOffering.NetworkMode networkMode; @@ -159,14 +156,6 @@ public class VpcOfferingVO implements VpcOffering { return isDefault; } - public boolean isForNsx() { - return forNsx; - } - - public void setForNsx(boolean forNsx) { - this.forNsx = forNsx; - } - public NetworkOffering.NetworkMode getNetworkMode() { return networkMode; } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java index e8ccc2ebcf1..e942eadb8ff 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java @@ -105,6 +105,9 @@ public class VpcVO implements Vpc { @Column(name = "ip6Dns2") String ip6Dns2; + @Column(name = "use_router_ip_resolver") + boolean useRouterIpResolver = false; + @Transient boolean rollingRestart = false; @@ -309,4 +312,13 @@ public class VpcVO implements Vpc { public String getIp6Dns2() { return ip6Dns2; } + + @Override + public boolean useRouterIpAsResolver() { + return useRouterIpResolver; + } + + public void setUseRouterIpResolver(boolean useRouterIpResolver) { + this.useRouterIpResolver = useRouterIpResolver; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java index 06cfd25e670..020536e97ec 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java @@ -18,6 +18,7 @@ package com.cloud.network.vpc.dao; import java.util.List; +import com.cloud.network.Network; import com.cloud.network.Network.Service; import com.cloud.network.vpc.VpcOfferingServiceMapVO; import com.cloud.utils.db.GenericDao; @@ -37,4 +38,8 @@ public interface VpcOfferingServiceMapDao extends GenericDao listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service); + } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java index c7400f6edfd..dcb1becf9e8 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.network.vpc.dao; import java.util.List; +import com.cloud.network.Network; import org.springframework.stereotype.Component; import com.cloud.network.Network.Service; @@ -110,4 +111,22 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase sc = AllFieldsSearch.create(); + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("provider", provider.getName()); + return findOneBy(sc) != null; + } + + @Override + public List listProvidersForServiceForVpcOffering(long vpcOfferingId, Service service) { + SearchCriteria sc = AllFieldsSearch.create(); + + sc.setParameters("vpcOffId", vpcOfferingId); + sc.setParameters("service", service.getName()); + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java index 753c45fcc78..a5c4c83ff0f 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcServiceMapDaoImpl.java @@ -68,8 +68,15 @@ public class VpcServiceMapDaoImpl extends GenericDaoBase @Override public boolean canProviderSupportServiceInVpc(long vpcId, Service service, Provider provider) { - // TODO Auto-generated method stub - return false; + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vpcId", vpcId); + sc.setParameters("service", service.getName()); + sc.setParameters("provider", provider.getName()); + if (findOneBy(sc) != null) { + return true; + } else { + return false; + } } @Override diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java index 5cad366945f..904c8e646eb 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java @@ -134,12 +134,6 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "for_vpc") boolean forVpc; - @Column(name = "for_tungsten") - boolean forTungsten = false; - - @Column(name = "for_nsx") - boolean forNsx = false; - @Column(name = "network_mode") NetworkMode networkMode; @@ -200,24 +194,6 @@ public class NetworkOfferingVO implements NetworkOffering { this.forVpc = isForVpc; } - @Override - public boolean isForTungsten() { - return forTungsten; - } - - public void setForTungsten(boolean forTungsten) { - this.forTungsten = forTungsten; - } - - @Override - public boolean isForNsx() { - return forNsx; - } - - public void setForNsx(boolean forNsx) { - this.forNsx = forNsx; - } - @Override public NetworkMode getNetworkMode() { return networkMode; diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java index 3724e03d9d0..cee5cc15e11 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDao.java @@ -22,10 +22,13 @@ import com.cloud.server.ResourceTag; import com.cloud.utils.db.GenericDao; import org.apache.cloudstack.api.response.ResourceIconResponse; +import java.util.Collection; import java.util.List; public interface ResourceIconDao extends GenericDao { ResourceIconResponse newResourceIconResponse(ResourceIcon resourceIconVO); ResourceIconVO findByResourceUuid(String resourceUuid, ResourceTag.ResourceObjectType resourceType); + List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, Collection resourceIds); + List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, Collection resourceUuids); List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType); } diff --git a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java index 1ae01bfc1ec..49a1fe7a560 100644 --- a/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/resource/icon/dao/ResourceIconDaoImpl.java @@ -24,8 +24,10 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.ResourceIconResponse; +import org.apache.commons.collections.CollectionUtils; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class ResourceIconDaoImpl extends GenericDaoBase implements ResourceIconDao { @@ -58,11 +60,36 @@ public class ResourceIconDaoImpl extends GenericDaoBase im } @Override - public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + public List listByResourceTypeAndIds(ResourceTag.ResourceObjectType resourceType, + Collection resourceIds) { + if (CollectionUtils.isEmpty(resourceIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceId", sb.entity().getResourceId(), SearchCriteria.Op.IN); + sb.and("resourceType", sb.entity().getResourceType(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("resourceId", resourceIds.toArray()); + sc.setParameters("resourceType", resourceType); + return listBy(sc); + } + + @Override + public List listByResourceTypeAndUuids(ResourceTag.ResourceObjectType resourceType, + Collection resourceUuids) { + if (CollectionUtils.isEmpty(resourceUuids)) { + return new ArrayList<>(); + } SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("uuid", resourceUuids.toArray()); sc.setParameters("resourceType", resourceType); - List resourceIcons = listBy(sc); + return listBy(sc); + } + + @Override + public List listResourceIcons(List resourceUuids, ResourceTag.ResourceObjectType resourceType) { + List resourceIcons = listByResourceTypeAndUuids(resourceType, resourceUuids); List iconResponses = new ArrayList<>(); for (ResourceIconVO resourceIcon : resourceIcons) { ResourceIconResponse response = new ResourceIconResponse(); diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java index 7f5c1a7afa1..cfe8049f5b2 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -124,6 +124,15 @@ public class ServiceOfferingVO implements ServiceOffering { @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled = true; + @Column(name = "vgpu_profile_id") + private Long vgpuProfileId; + + @Column(name = "gpu_count") + private Integer gpuCount; + + @Column(name = "gpu_display") + private Boolean gpuDisplay; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. // Call service offering dao to load it. @@ -198,6 +207,8 @@ public class ServiceOfferingVO implements ServiceOffering { systemUse = offering.isSystemUse(); dynamicScalingEnabled = offering.isDynamicScalingEnabled(); diskOfferingStrictness = offering.diskOfferingStrictness; + vgpuProfileId = offering.vgpuProfileId; + gpuCount = offering.gpuCount; } @Override @@ -445,4 +456,30 @@ public class ServiceOfferingVO implements ServiceOffering { public void setDiskOfferingStrictness(boolean diskOfferingStrictness) { this.diskOfferingStrictness = diskOfferingStrictness; } + + @Override + public Long getVgpuProfileId() { + return vgpuProfileId; + } + + public void setVgpuProfileId(Long vgpuProfileId) { + this.vgpuProfileId = vgpuProfileId; + } + + @Override + public Integer getGpuCount() { + return gpuCount; + } + + public void setGpuCount(Integer gpuCount) { + this.gpuCount = gpuCount; + } + + public Boolean getGpuDisplay() { + return gpuDisplay; + } + + public void setGpuDisplay(Boolean gpuDisplay) { + this.gpuDisplay = gpuDisplay; + } } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java index ceb5b0a4fc1..d3bab4fcbe2 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java @@ -22,6 +22,7 @@ import java.util.Map; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.db.GenericDao; +import com.cloud.utils.db.SearchBuilder; import com.cloud.vm.VirtualMachine; /* @@ -57,4 +58,6 @@ public interface ServiceOfferingDao extends GenericDao ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId, boolean includingRemoved); List listIdsByHostTag(String tag); + + void addCheckForGpuEnabled(SearchBuilder serviceOfferingSearch, Boolean gpuEnabled); } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java index 803522fa6aa..f360770ad68 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -39,7 +39,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; -import com.cloud.vm.dao.UserVmDetailsDao; +import com.cloud.vm.dao.VMInstanceDetailsDao; @Component @DB() @@ -48,7 +48,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map dynamicOffering = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); return getComputeOffering(offering, dynamicOffering); } return offering; @@ -190,7 +190,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + Map dynamicOffering = vmInstanceDetailsDao.listDetailsKeyPairs(vmId); return getComputeOffering(offering, dynamicOffering); } return offering; @@ -312,4 +312,13 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase serviceOfferingSearch, Boolean gpuEnabled) { + if (gpuEnabled) { + serviceOfferingSearch.and("gpuEnabled", serviceOfferingSearch.entity().getVgpuProfileId(), SearchCriteria.Op.NNULL); + } else { + serviceOfferingSearch.and("gpuDisabled", serviceOfferingSearch.entity().getVgpuProfileId(), SearchCriteria.Op.NULL); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java b/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java index 36773e351e3..642705ffcbe 100644 --- a/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/GuestOSCategoryVO.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -25,6 +26,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "guest_os_category") public class GuestOSCategoryVO implements GuestOsCategory { @@ -39,6 +42,26 @@ public class GuestOSCategoryVO implements GuestOsCategory { @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); + @Column(name = "featured") + boolean featured; + + @Column(name = "sort_key") + private int sortKey; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + public GuestOSCategoryVO() { + } + + public GuestOSCategoryVO(String name, boolean featured) { + this.name = name; + this.featured = featured; + } + @Override public long getId() { return id; @@ -59,7 +82,25 @@ public class GuestOSCategoryVO implements GuestOsCategory { return this.uuid; } - public void setUuid(String uuid) { - this.uuid = uuid; + @Override + public boolean isFeatured() { + return featured; + } + + public void setFeatured(Boolean featured) { + this.featured = featured; + } + + public void setSortKey(int key) { + sortKey = key; + } + + public int getSortKey() { + return sortKey; + } + + @Override + public Date getCreated() { + return created; } } diff --git a/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java b/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java index 19c67a91e2c..4a504333344 100644 --- a/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/SnapshotVO.java @@ -284,6 +284,6 @@ public class SnapshotVO implements Snapshot { public String toString() { return String.format("Snapshot %s", ReflectionToStringBuilderUtils.reflectOnlySelectedFields( - this, "id", "uuid", "name", "volumeId", "version")); + this, "id", "uuid", "name", "volumeId", "version", "state")); } } diff --git a/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java new file mode 100644 index 00000000000..5690324340c --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/StoragePoolAndAccessGroupMapVO.java @@ -0,0 +1,64 @@ +// 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.storage; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "storage_pool_and_access_group_map") +public class StoragePoolAndAccessGroupMapVO implements InternalIdentity { + + protected StoragePoolAndAccessGroupMapVO() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "pool_id") + private long poolId; + + @Column(name = "storage_access_group") + private String storageAccessGroup; + + public StoragePoolAndAccessGroupMapVO(long poolId, String storageAccessGroup) { + this.poolId = poolId; + this.storageAccessGroup = storageAccessGroup; + } + + @Override + public long getId() { + return this.id; + } + + public long getPoolId() { + return poolId; + } + + public String getStorageAccessGroup() { + return storageAccessGroup; + } + +} diff --git a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java index 10d08601515..88d3b7ba2d8 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VMTemplateVO.java @@ -34,6 +34,7 @@ import javax.persistence.Transient; import com.cloud.cpu.CPU; import com.cloud.user.UserData; +import com.cloud.utils.UuidUtils; import org.apache.cloudstack.util.CPUArchConverter; import org.apache.cloudstack.util.HypervisorTypeConverter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; @@ -162,6 +163,9 @@ public class VMTemplateVO implements VirtualMachineTemplate { @Column(name = "deploy_as_is") private boolean deployAsIs; + @Column(name = "for_cks") + private boolean forCks; + @Column(name = "user_data_id") private Long userDataId; @@ -173,6 +177,9 @@ public class VMTemplateVO implements VirtualMachineTemplate { @Convert(converter = CPUArchConverter.class) private CPU.CPUArch arch; + @Column(name = "extension_id") + private Long extensionId; + @Override public String getUniqueName() { return uniqueName; @@ -215,7 +222,7 @@ public class VMTemplateVO implements VirtualMachineTemplate { public VMTemplateVO(long id, String name, ImageFormat format, boolean isPublic, boolean featured, boolean isExtractable, TemplateType type, String url, boolean requiresHvm, int bits, long accountId, String cksum, String displayText, boolean enablePassword, long guestOSId, boolean bootable, HypervisorType hyperType, String templateTag, Map details, boolean sshKeyEnabled, boolean isDynamicallyScalable, boolean directDownload, - boolean deployAsIs, CPU.CPUArch arch) { + boolean deployAsIs, CPU.CPUArch arch, Long extensionId) { this(id, name, format, @@ -242,6 +249,7 @@ public class VMTemplateVO implements VirtualMachineTemplate { this.directDownload = directDownload; this.deployAsIs = deployAsIs; this.arch = arch; + this.extensionId = extensionId; } public static VMTemplateVO createPreHostIso(Long id, String uniqueName, String name, ImageFormat format, boolean isPublic, boolean featured, TemplateType type, @@ -337,7 +345,7 @@ public class VMTemplateVO implements VirtualMachineTemplate { name.append("-"); name.append(userId); name.append("-"); - name.append(UUID.nameUUIDFromBytes((displayName + System.currentTimeMillis()).getBytes()).toString()); + name.append(UuidUtils.nameUUIDFromBytes((displayName + System.currentTimeMillis()).getBytes()).toString()); return name.toString(); } @@ -664,6 +672,14 @@ public class VMTemplateVO implements VirtualMachineTemplate { this.deployAsIs = deployAsIs; } + public boolean isForCks() { + return forCks; + } + + public void setForCks(boolean forCks) { + this.forCks = forCks; + } + @Override public Long getUserDataId() { return userDataId; @@ -691,4 +707,11 @@ public class VMTemplateVO implements VirtualMachineTemplate { this.arch = arch; } + public Long getExtensionId() { + return extensionId; + } + + public void setExtensionId(Long extensionId) { + this.extensionId = extensionId; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java index 6fad6c5c47e..cc1c96aee52 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSCategoryDaoImpl.java @@ -27,7 +27,6 @@ import com.cloud.utils.db.GenericDaoBase; public class GuestOSCategoryDaoImpl extends GenericDaoBase implements GuestOSCategoryDao { protected GuestOSCategoryDaoImpl() { - } @Override diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java index 13cd398073a..1a2b098c40a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDao.java @@ -16,14 +16,14 @@ // under the License. package com.cloud.storage.dao; +import java.util.List; +import java.util.Set; + import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; -import java.util.List; -import java.util.Set; - public interface GuestOSDao extends GenericDao { GuestOSVO findOneByDisplayName(String displayName); @@ -36,4 +36,6 @@ public interface GuestOSDao extends GenericDao { List listByDisplayName(String displayName); Pair, Integer> listGuestOSByCriteria(Long startIndex, Long pageSize, Long id, Long osCategoryId, String description, String keyword, Boolean forDisplay); + + List listIdsByCategoryId(final long categoryId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java index efcaa482a67..881be207c1a 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/GuestOSDaoImpl.java @@ -25,19 +25,20 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import com.cloud.storage.GuestOS; -import com.cloud.utils.Pair; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; +import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; @Component public class GuestOSDaoImpl extends GenericDaoBase implements GuestOSDao { @@ -152,4 +153,14 @@ public class GuestOSDaoImpl extends GenericDaoBase implements G return new Pair<>(result.first(), result.second()); } + @Override + public List listIdsByCategoryId(final long categoryId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("categoryId", sb.entity().getCategoryId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("categoryId", categoryId); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java index 171634fb104..3cda7d42760 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDao.java @@ -58,4 +58,6 @@ public interface SnapshotDao extends GenericDao, StateDao listByIds(Object... ids); List searchByVolumes(List volumeIds); + + List listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... type); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java index f5fc9c47d03..c479a386d79 100755 --- a/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.storage.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.annotation.PostConstruct; @@ -56,6 +57,10 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private static final String GET_LAST_SNAPSHOT = "SELECT snapshots.id FROM snapshot_store_ref, snapshots where snapshots.id = snapshot_store_ref.snapshot_id AND snapshosts.volume_id = ? AND snapshot_store_ref.role = ? ORDER BY created DESC"; + private static final String VOLUME_ID = "volumeId"; + private static final String NOT_TYPE = "notType"; + private static final String STATUS = "status"; + private SearchBuilder snapshotIdsSearch; private SearchBuilder VolumeIdSearch; private SearchBuilder VolumeIdTypeSearch; @@ -66,6 +71,8 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private SearchBuilder StatusSearch; private SearchBuilder notInStatusSearch; private GenericSearchBuilder CountSnapshotsByAccount; + + private SearchBuilder volumeIdAndTypeNotInSearch; @Inject ResourceTagDao _tagsDao; @Inject @@ -181,6 +188,12 @@ public class SnapshotDaoImpl extends GenericDaoBase implements InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); InstanceIdSearch.done(); + + volumeIdAndTypeNotInSearch = createSearchBuilder(); + volumeIdAndTypeNotInSearch.and(VOLUME_ID, volumeIdAndTypeNotInSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); + volumeIdAndTypeNotInSearch.and(STATUS, volumeIdAndTypeNotInSearch.entity().getState(), SearchCriteria.Op.NEQ); + volumeIdAndTypeNotInSearch.and(NOT_TYPE, volumeIdAndTypeNotInSearch.entity().getTypeDescription(), SearchCriteria.Op.NOTIN); + volumeIdAndTypeNotInSearch.done(); } @Override @@ -299,4 +312,14 @@ public class SnapshotDaoImpl extends GenericDaoBase implements sc.setParameters("volumeIds", volumeIds.toArray()); return search(sc, null); } + + @Override + public List listByVolumeIdAndTypeNotInAndStateNotRemoved(long volumeId, Type... types) { + SearchCriteria sc = volumeIdAndTypeNotInSearch.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(NOT_TYPE, Arrays.stream(types).map(Type::toString).toArray()); + sc.setParameters(STATUS, State.Destroyed); + + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java new file mode 100644 index 00000000000..3ff797f7e74 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDao.java @@ -0,0 +1,31 @@ +// 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.storage.dao; + +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDao; + +public interface StoragePoolAndAccessGroupMapDao extends GenericDao { + + void persist(long poolId, List storageAccessGroups); + List getStorageAccessGroups(long poolId); + void deleteStorageAccessGroups(long poolId); + List listDistinctStorageAccessGroups(String name, String keyword); +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java new file mode 100644 index 00000000000..63e82b79748 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolAndAccessGroupMapDaoImpl.java @@ -0,0 +1,105 @@ +// 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.storage.dao; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; + +public class StoragePoolAndAccessGroupMapDaoImpl extends GenericDaoBase implements StoragePoolAndAccessGroupMapDao { + + protected final SearchBuilder StoragePoolAccessGroupSearch; + + public StoragePoolAndAccessGroupMapDaoImpl() { + StoragePoolAccessGroupSearch = createSearchBuilder(); + StoragePoolAccessGroupSearch.and("poolId", StoragePoolAccessGroupSearch.entity().getPoolId(), SearchCriteria.Op.EQ); + StoragePoolAccessGroupSearch.done(); + } + + @Override + public void persist(long poolId, List storageAccessGroups) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + + for (String sag : storageAccessGroups) { + sag = sag.trim(); + if (sag.length() > 0) { + StoragePoolAndAccessGroupMapVO vo = new StoragePoolAndAccessGroupMapVO(poolId, sag); + persist(vo); + } + } + txn.commit(); + } + + @Override + public List getStorageAccessGroups(long poolId) { + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + + List results = search(sc, null); + List storagePoolAccessGroups = new ArrayList(results.size()); + for (StoragePoolAndAccessGroupMapVO result : results) { + storagePoolAccessGroups.add(result.getStorageAccessGroup()); + } + + return storagePoolAccessGroups; + } + + @Override + public void deleteStorageAccessGroups(long poolId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = StoragePoolAccessGroupSearch.create(); + sc.setParameters("poolId", poolId); + expunge(sc); + txn.commit(); + } + + @Override + public List listDistinctStorageAccessGroups(String name, String keyword) { + GenericSearchBuilder searchBuilder = createSearchBuilder(String.class); + + searchBuilder.select(null, SearchCriteria.Func.DISTINCT, searchBuilder.entity().getStorageAccessGroup()); + searchBuilder.and("name", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + searchBuilder.and("keyword", searchBuilder.entity().getStorageAccessGroup(), SearchCriteria.Op.LIKE); + searchBuilder.done(); + + SearchCriteria sc = searchBuilder.create(); + + if (name != null) { + sc.setParameters("name", name); + } + + if (keyword != null) { + sc.setParameters("keyword", "%" + keyword + "%"); + } + + return customSearch(sc, null); + } + +} diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java index 639c2571541..94e13ba2d56 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDao.java @@ -30,6 +30,8 @@ public interface StoragePoolHostDao extends GenericDao public StoragePoolHostVO findByPoolHost(long poolId, long hostId); + List findByLocalPath(String path); + List listByHostStatus(long poolId, Status hostStatus); List findHostsConnectedToPools(List poolIds); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java index 5a466af348c..55b5668bbc1 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/StoragePoolHostDaoImpl.java @@ -45,6 +45,7 @@ public class StoragePoolHostDaoImpl extends GenericDaoBase PoolSearch; protected final SearchBuilder HostSearch; protected final SearchBuilder PoolHostSearch; + protected final SearchBuilder LocalPathSearch; protected SearchBuilder poolNotInClusterSearch; @@ -77,6 +78,9 @@ public class StoragePoolHostDaoImpl extends GenericDaoBase findByLocalPath(String path) { + SearchCriteria sc = LocalPathSearch.create(); + sc.setParameters("local_path", path); + return listBy(sc); + } + @Override public List listByHostStatus(long poolId, Status hostStatus) { TransactionLegacy txn = TransactionLegacy.currentTxn(); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java index 3ac514530ce..6785c365329 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java @@ -19,6 +19,7 @@ package com.cloud.storage.dao; import java.util.List; import java.util.Map; +import com.cloud.cpu.CPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; @@ -57,6 +58,8 @@ public interface VMTemplateDao extends GenericDao, StateDao< public List listInZoneByState(long dataCenterId, VirtualMachineTemplate.State... states); + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, Boolean isVnf); + public List listAllActive(); public List listByState(VirtualMachineTemplate.State... states); @@ -71,11 +74,15 @@ public interface VMTemplateDao extends GenericDao, StateDao< VMTemplateVO findSystemVMTemplate(long zoneId); - VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType); + VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch); + + List findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, String preferredArch); VMTemplateVO findRoutingTemplate(HypervisorType type, String templateName); - VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, Storage.TemplateType type); + List findRoutingTemplates(HypervisorType type, String templateName, String preferredArch); + + VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, Storage.TemplateType type); public Long countTemplatesForAccount(long accountId); @@ -87,11 +94,13 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listByParentTemplatetId(long parentTemplatetId); - VMTemplateVO findLatestTemplateByName(String name); + VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch); List findTemplatesLinkedToUserdata(long userdataId); List listByIds(List ids); List listIdsByTemplateTag(String tag); + + List listIdsByExtensionId(long extensionId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java index 7513848536b..08b82cbb45b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -17,10 +17,14 @@ package com.cloud.storage.dao; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; @@ -28,8 +32,10 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import com.cloud.cpu.CPU; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; import com.cloud.host.Host; @@ -47,6 +53,7 @@ import com.cloud.storage.VMTemplateZoneVO; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; @@ -111,6 +118,7 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem LatestTemplateByHypervisorTypeSearch = createSearchBuilder(); LatestTemplateByHypervisorTypeSearch.and("hypervisorType", LatestTemplateByHypervisorTypeSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("templateType", LatestTemplateByHypervisorTypeSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); + LatestTemplateByHypervisorTypeSearch.and("arch", LatestTemplateByHypervisorTypeSearch.entity().getArch(), SearchCriteria.Op.EQ); LatestTemplateByHypervisorTypeSearch.and("removed", LatestTemplateByHypervisorTypeSearch.entity().getRemoved(), SearchCriteria.Op.NULL); } @@ -238,10 +246,16 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem @Override - public VMTemplateVO findLatestTemplateByName(String name) { - SearchCriteria sc = createSearchCriteria(); - sc.addAnd("name", SearchCriteria.Op.EQ, name); - sc.addAnd("removed", SearchCriteria.Op.NULL); + public VMTemplateVO findLatestTemplateByName(String name, CPU.CPUArch arch) { + SearchBuilder sb = createSearchBuilder(); + sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("name", name); + if (arch != null) { + sc.setParameters("arch", arch); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if ((templates != null) && !templates.isEmpty()) { @@ -508,6 +522,48 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem return listBy(sc); } + @Override + public List listTemplateIsoByArchVnfAndZone(Long dataCenterId, CPU.CPUArch arch, Boolean isIso, + Boolean isVnf) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.select(null, Func.DISTINCT, sb.entity().getGuestOSId()); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.IN); + sb.and("type", sb.entity().getTemplateType(), SearchCriteria.Op.IN); + sb.and("arch", sb.entity().getArch(), SearchCriteria.Op.EQ); + if (isIso != null) { + sb.and("isIso", sb.entity().getFormat(), isIso ? SearchCriteria.Op.EQ : SearchCriteria.Op.NEQ); + } + if (dataCenterId != null) { + SearchBuilder templateZoneSearch = _templateZoneDao.createSearchBuilder(); + templateZoneSearch.and("removed", templateZoneSearch.entity().getRemoved(), SearchCriteria.Op.NULL); + templateZoneSearch.and("zoneId", templateZoneSearch.entity().getZoneId(), SearchCriteria.Op.EQ); + sb.join("templateZoneSearch", templateZoneSearch, templateZoneSearch.entity().getTemplateId(), + sb.entity().getId(), JoinBuilder.JoinType.INNER); + templateZoneSearch.done(); + } + sb.done(); + SearchCriteria sc = sb.create(); + List types = new ArrayList<>(Arrays.asList(TemplateType.USER, TemplateType.BUILTIN, + TemplateType.PERHOST)); + if (isVnf == null) { + types.add(TemplateType.VNF); + } else if (isVnf) { + types = Collections.singletonList(TemplateType.VNF); + } + sc.setParameters("type", types.toArray()); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + if (dataCenterId != null) { + sc.setJoinParameters("templateZoneSearch", "zoneId", dataCenterId); + } + if (arch != null) { + sc.setParameters("arch", arch); + } + if (isIso != null) { + sc.setParameters("isIso", ImageFormat.ISO); + } + return customSearch(sc, null); + } + @Override public List listAllActive() { SearchCriteria sc = ActiveTmpltSearch.create(); @@ -566,11 +622,19 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem } @Override - public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType) { + public VMTemplateVO findSystemVMReadyTemplate(long zoneId, HypervisorType hypervisorType, String preferredArch) { List templates = listAllReadySystemVMTemplates(zoneId); if (CollectionUtils.isEmpty(templates)) { return null; } + if (StringUtils.isNotBlank(preferredArch)) { + // Sort the templates by preferred architecture first + templates = templates.stream() + .sorted(Comparator.comparing( + x -> !x.getArch().getType().equalsIgnoreCase(preferredArch) + )) + .collect(Collectors.toList()); + } if (hypervisorType == HypervisorType.Any) { return templates.get(0); } @@ -580,6 +644,59 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem .orElse(null); } + protected List getSortedTemplatesListWithPreferredArch( + Map, VMTemplateVO> uniqueTemplates, String preferredArch) { + List result = new ArrayList<>(uniqueTemplates.values()); + if (StringUtils.isNotBlank(preferredArch)) { + result.sort((t1, t2) -> { + boolean t1Preferred = t1.getArch().getType().equalsIgnoreCase(preferredArch); + boolean t2Preferred = t2.getArch().getType().equalsIgnoreCase(preferredArch); + if (t1Preferred && !t2Preferred) { + return -1; // t1 comes before t2 + } else if (!t1Preferred && t2Preferred) { + return 1; // t2 comes before t1 + } else { + // Both are either preferred or not preferred; use template id as a secondary sorting key. + return Long.compare(t1.getId(), t2.getId()); + } + }); + } else { + result.sort(Comparator.comparing(VMTemplateVO::getId).reversed()); + } + return result; + } + + @Override + public List findSystemVMReadyTemplates(long zoneId, HypervisorType hypervisorType, + String preferredArch) { + List> availableHypervisors = _hostDao.listDistinctHypervisorArchTypes(zoneId); + if (CollectionUtils.isEmpty(availableHypervisors)) { + return Collections.emptyList(); + } + SearchCriteria sc = readySystemTemplateSearch.create(); + sc.setParameters("templateType", Storage.TemplateType.SYSTEM); + sc.setParameters("state", VirtualMachineTemplate.State.Active); + if (hypervisorType != null && !HypervisorType.Any.equals(hypervisorType)) { + sc.setParameters("hypervisorType", List.of(hypervisorType).toArray()); + } else { + sc.setParameters("hypervisorType", + availableHypervisors.stream().map(Pair::first).distinct().toArray()); + } + sc.setJoinParameters("vmTemplateJoinTemplateStoreRef", "downloadState", + List.of(VMTemplateStorageResourceAssoc.Status.DOWNLOADED, + VMTemplateStorageResourceAssoc.Status.BYPASSED).toArray()); + // order by descending order of id + List templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, null)); + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + for (VMTemplateVO template : templates) { + Pair key = new Pair<>(template.getHypervisorType(), template.getArch()); + if (availableHypervisors.contains(key) && !uniqueTemplates.containsKey(key)) { + uniqueTemplates.put(key, template); + } + } + return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch); + } + @Override public VMTemplateVO findRoutingTemplate(HypervisorType hType, String templateName) { SearchCriteria sc = tmpltTypeHyperSearch2.create(); @@ -618,10 +735,43 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem } @Override - public VMTemplateVO findLatestTemplateByTypeAndHypervisor(HypervisorType hypervisorType, TemplateType type) { + public List findRoutingTemplates(HypervisorType hType, String templateName, String preferredArch) { + SearchCriteria sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.ROUTING); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + List templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + if (CollectionUtils.isEmpty(templates)) { + sc = tmpltTypeHyperSearch2.create(); + sc.setParameters("templateType", TemplateType.SYSTEM); + sc.setParameters("hypervisorType", hType); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + if (templateName != null) { + sc.setParameters("templateName", templateName); + } + templates = listBy(sc, new Filter(VMTemplateVO.class, "id", false, null, 1L)); + } + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + for (VMTemplateVO template : templates) { + Pair key = new Pair<>(template.getHypervisorType(), template.getArch()); + if (!uniqueTemplates.containsKey(key)) { + uniqueTemplates.put(key, template); + } + } + return getSortedTemplatesListWithPreferredArch(uniqueTemplates, preferredArch); + } + + @Override + public VMTemplateVO findLatestTemplateByTypeAndHypervisorAndArch(HypervisorType hypervisorType, CPU.CPUArch arch, TemplateType type) { SearchCriteria sc = LatestTemplateByHypervisorTypeSearch.create(); sc.setParameters("hypervisorType", hypervisorType); sc.setParameters("templateType", type); + if (arch != null) { + sc.setParameters("arch", arch); + } Filter filter = new Filter(VMTemplateVO.class, "id", false, null, 1L); List templates = listBy(sc, filter); if (templates != null && !templates.isEmpty()) { @@ -696,6 +846,17 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem return customSearchIncludingRemoved(sc, null); } + @Override + public List listIdsByExtensionId(long extensionId) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getId()); + sb.and("extensionId", sb.entity().getExtensionId(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("extensionId", extensionId); + return customSearch(sc, null); + } + @Override public boolean updateState( com.cloud.template.VirtualMachineTemplate.State currentState, diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java index e6ffca06f9e..0f47c9e7155 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -162,4 +162,6 @@ public interface VolumeDao extends GenericDao, StateDao searchRemovedByVms(List vmIds, Long batchSize); VolumeVO findOneByIScsiName(String iScsiName); + + VolumeVO findByLastIdAndState(long lastVolumeId, Volume.State...states); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index 750dbf2bee0..f8b6bb3ed68 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -49,6 +49,7 @@ import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; @@ -397,6 +398,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), Op.EQ); AllFieldsSearch.and("passphraseId", AllFieldsSearch.entity().getPassphraseId(), Op.EQ); AllFieldsSearch.and("iScsiName", AllFieldsSearch.entity().get_iScsiName(), Op.EQ); + AllFieldsSearch.and("path", AllFieldsSearch.entity().getPath(), Op.EQ); AllFieldsSearch.done(); RootDiskStateSearch = createSearchBuilder(); @@ -904,9 +906,18 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol return searchIncludingRemoved(sc, filter, null, false); } + @Override public VolumeVO findOneByIScsiName(String iScsiName) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("iScsiName", iScsiName); return findOneIncludingRemovedBy(sc); } + + @Override + public VolumeVO findByLastIdAndState(long lastVolumeId, State ...states) { + QueryBuilder sc = QueryBuilder.create(VolumeVO.class); + sc.and(sc.entity().getLastId(), SearchCriteria.Op.EQ, lastVolumeId); + sc.and(sc.entity().getState(), SearchCriteria.Op.IN, (Object[]) states); + return sc.find(); + } } 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 1e3b3a7e5ec..c211b3e9728 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java @@ -90,6 +90,7 @@ import com.cloud.upgrade.dao.Upgrade41900to41910; import com.cloud.upgrade.dao.Upgrade41910to42000; import com.cloud.upgrade.dao.Upgrade42000to42010; import com.cloud.upgrade.dao.Upgrade42010to42100; +import com.cloud.upgrade.dao.Upgrade42100to42200; import com.cloud.upgrade.dao.Upgrade420to421; import com.cloud.upgrade.dao.Upgrade421to430; import com.cloud.upgrade.dao.Upgrade430to440; @@ -234,6 +235,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { .next("4.19.1.0", new Upgrade41910to42000()) .next("4.20.0.0", new Upgrade42000to42010()) .next("4.20.1.0", new Upgrade42010to42100()) + .next("4.21.0.0", new Upgrade42100to42200()) .build(); } @@ -379,6 +381,9 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker { } finally { txn.close(); } + if (upgrade.refreshPoolConnectionsAfterUpgrade()) { + TransactionLegacy.refreshConnections(TransactionLegacy.CLOUD_DB); + } return version; } 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 9b0e10cbbfc..9b1420f22c3 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/SystemVmTemplateRegistration.java @@ -16,36 +16,29 @@ // under the License. package com.cloud.upgrade; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.dao.ClusterDao; -import com.cloud.dc.dao.ClusterDaoImpl; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.dc.dao.DataCenterDaoImpl; -import com.cloud.hypervisor.Hypervisor; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.Storage; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.VMTemplateStorageResourceAssoc; -import com.cloud.storage.VMTemplateVO; -import com.cloud.storage.VMTemplateZoneVO; -import com.cloud.storage.dao.VMTemplateDao; -import com.cloud.storage.dao.VMTemplateDaoImpl; -import com.cloud.storage.dao.VMTemplateZoneDao; -import com.cloud.storage.dao.VMTemplateZoneDaoImpl; -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.Pair; -import com.cloud.utils.UriUtils; -import com.cloud.utils.db.GlobalLock; -import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.script.Script; -import com.cloud.vm.dao.VMInstanceDao; -import com.cloud.vm.dao.VMInstanceDaoImpl; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Connection; +import java.sql.Date; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; + +import javax.inject.Inject; + import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; @@ -59,35 +52,49 @@ import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.utils.security.DigestHelper; import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ini4j.Ini; -import javax.inject.Inject; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.sql.Connection; -import java.sql.Date; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; +import com.cloud.cpu.CPU; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.dc.dao.ClusterDaoImpl; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DataCenterDaoImpl; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Storage; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.VMTemplateStorageResourceAssoc; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.GuestOSDaoImpl; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateDaoImpl; +import com.cloud.storage.dao.VMTemplateZoneDao; +import com.cloud.storage.dao.VMTemplateZoneDaoImpl; +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.HttpUtils; +import com.cloud.utils.Pair; +import com.cloud.utils.UriUtils; +import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.dao.VMInstanceDaoImpl; public class SystemVmTemplateRegistration { protected static Logger LOGGER = LogManager.getLogger(SystemVmTemplateRegistration.class); - private static final String MOUNT_COMMAND = "sudo mount -t nfs %s %s"; + private static final String MOUNT_COMMAND_BASE = "sudo mount -t nfs"; private static final String UMOUNT_COMMAND = "sudo umount %s"; private static final String RELATIVE_TEMPLATE_PATH = "./engine/schema/dist/systemvm-templates/"; private static final String ABSOLUTE_TEMPLATE_PATH = "/usr/share/cloudstack-management/templates/systemvm/"; @@ -98,11 +105,12 @@ public class SystemVmTemplateRegistration { private static final String PARTIAL_TEMPLATE_FOLDER = String.format("/template/tmpl/%d/", Account.ACCOUNT_ID_SYSTEM); private static final String storageScriptsDir = "scripts/storage/secondary"; private static final Integer OTHER_LINUX_ID = 99; - private static final Integer LINUX_5_ID = 15; - private static final Integer LINUX_7_ID = 183; + private static Integer LINUX_12_ID = 363; private static final Integer SCRIPT_TIMEOUT = 1800000; private static final Integer LOCK_WAIT_TIMEOUT = 1200; - + protected static final List DOWNLOADABLE_TEMPLATE_ARCH_TYPES = Arrays.asList( + CPU.CPUArch.arm64 + ); public static String CS_MAJOR_VERSION = null; public static String CS_TINY_VERSION = null; @@ -125,9 +133,13 @@ public class SystemVmTemplateRegistration { ClusterDao clusterDao; @Inject ConfigurationDao configurationDao; + @Inject + private GuestOSDao guestOSDao; private String systemVmTemplateVersion; + private final File tempDownloadDir; + public SystemVmTemplateRegistration() { dataCenterDao = new DataCenterDaoImpl(); vmTemplateDao = new VMTemplateDaoImpl(); @@ -138,6 +150,8 @@ public class SystemVmTemplateRegistration { imageStoreDetailsDao = new ImageStoreDetailsDaoImpl(); clusterDao = new ClusterDaoImpl(); configurationDao = new ConfigurationDaoImpl(); + guestOSDao = new GuestOSDaoImpl(); + tempDownloadDir = new File(System.getProperty("java.io.tmpdir")); } /** @@ -149,7 +163,7 @@ public class SystemVmTemplateRegistration { } public static String getMountCommand(String nfsVersion, String device, String dir) { - String cmd = "sudo mount -t nfs"; + String cmd = MOUNT_COMMAND_BASE; if (StringUtils.isNotBlank(nfsVersion)) { cmd = String.format("%s -o vers=%s", cmd, nfsVersion); } @@ -163,6 +177,10 @@ public class SystemVmTemplateRegistration { return systemVmTemplateVersion; } + public File getTempDownloadDir() { + return tempDownloadDir; + } + private static class SystemVMTemplateDetails { Long id; String uuid; @@ -174,6 +192,7 @@ public class SystemVmTemplateRegistration { ImageFormat format; Integer guestOsId; Hypervisor.HypervisorType hypervisorType; + CPU.CPUArch arch; Long storeId; Long size; Long physicalSize; @@ -183,7 +202,7 @@ public class SystemVmTemplateRegistration { SystemVMTemplateDetails(String uuid, String name, Date created, String url, String checksum, ImageFormat format, Integer guestOsId, Hypervisor.HypervisorType hypervisorType, - Long storeId) { + CPU.CPUArch arch, Long storeId) { this.uuid = uuid; this.name = name; this.created = created; @@ -192,6 +211,7 @@ public class SystemVmTemplateRegistration { this.format = format; this.guestOsId = guestOsId; this.hypervisorType = hypervisorType; + this.arch = arch; this.storeId = storeId; } @@ -235,6 +255,10 @@ public class SystemVmTemplateRegistration { return hypervisorType; } + public CPU.CPUArch getArch() { + return arch; + } + public Long getStoreId() { return storeId; } @@ -288,20 +312,19 @@ public class SystemVmTemplateRegistration { } } - public static final List hypervisorList = Arrays.asList(Hypervisor.HypervisorType.KVM, - Hypervisor.HypervisorType.VMware, - Hypervisor.HypervisorType.XenServer, - Hypervisor.HypervisorType.Hyperv, - Hypervisor.HypervisorType.LXC, - Hypervisor.HypervisorType.Ovm3 + public static final List> hypervisorList = Arrays.asList( + new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), + new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), + new Pair<>(Hypervisor.HypervisorType.VMware, null), + new Pair<>(Hypervisor.HypervisorType.XenServer, null), + new Pair<>(Hypervisor.HypervisorType.Hyperv, null), + new Pair<>(Hypervisor.HypervisorType.LXC, null), + new Pair<>(Hypervisor.HypervisorType.Ovm3, null) ); - public static final Map NewTemplateNameList = new HashMap(); - public static final Map FileNames = new HashMap(); - public static final Map NewTemplateUrl = new HashMap(); - public static final Map NewTemplateChecksum = new HashMap(); + public static final Map NewTemplateMap = new HashMap<>(); - public static final Map RouterTemplateConfigurationNames = new HashMap() { + public static final Map RouterTemplateConfigurationNames = new HashMap<>() { { put(Hypervisor.HypervisorType.KVM, "router.template.kvm"); put(Hypervisor.HypervisorType.VMware, "router.template.vmware"); @@ -312,14 +335,14 @@ public class SystemVmTemplateRegistration { } }; - public static final Map hypervisorGuestOsMap = new HashMap() { + public static Map hypervisorGuestOsMap = new HashMap<>() { { - put(Hypervisor.HypervisorType.KVM, LINUX_5_ID); + put(Hypervisor.HypervisorType.KVM, LINUX_12_ID); put(Hypervisor.HypervisorType.XenServer, OTHER_LINUX_ID); put(Hypervisor.HypervisorType.VMware, OTHER_LINUX_ID); - put(Hypervisor.HypervisorType.Hyperv, LINUX_5_ID); - put(Hypervisor.HypervisorType.LXC, LINUX_5_ID); - put(Hypervisor.HypervisorType.Ovm3, LINUX_7_ID); + put(Hypervisor.HypervisorType.Hyperv, LINUX_12_ID); + put(Hypervisor.HypervisorType.LXC, LINUX_12_ID); + put(Hypervisor.HypervisorType.Ovm3, LINUX_12_ID); } }; @@ -368,55 +391,73 @@ public class SystemVmTemplateRegistration { } } - public Long getRegisteredTemplateId(Pair hypervisorAndTemplateName) { - VMTemplateVO vmTemplate = vmTemplateDao.findLatestTemplateByName(hypervisorAndTemplateName.second()); - Long templateId = null; - if (vmTemplate != null) { - templateId = vmTemplate.getId(); + private static String getHypervisorArchLog(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) { + StringBuilder sb = new StringBuilder("hypervisor: ").append(hypervisorType.name()); + if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + sb.append(", arch: ").append(arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); } - return templateId; + return sb.toString(); } - private static String fetchTemplatesPath() { - String filePath = RELATIVE_TEMPLATE_PATH + METADATA_FILE_NAME; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - File metaFile = new File(filePath); - String templatePath = null; - if (metaFile.exists()) { - templatePath = RELATIVE_TEMPLATE_PATH; + protected static String getHypervisorArchKey(Hypervisor.HypervisorType hypervisorType, CPU.CPUArch arch) { + if (Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + return String.format("%s-%s", hypervisorType.name().toLowerCase(), + arch == null ? CPU.CPUArch.amd64.getType() : arch.getType()); } - if (templatePath == null) { - filePath = ABSOLUTE_TEMPLATE_PATH + METADATA_FILE_NAME; - metaFile = new File(filePath); - templatePath = ABSOLUTE_TEMPLATE_PATH; - LOGGER.debug(String.format("Looking for file [ %s ] in the classpath.", filePath)); - if (!metaFile.exists()) { - String errMsg = String.format("Unable to locate metadata file in your setup at %s", filePath.toString()); - LOGGER.error(errMsg); - throw new CloudRuntimeException(errMsg); + return hypervisorType.name().toLowerCase(); + } + + protected static MetadataTemplateDetails getMetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, + CPU.CPUArch arch) { + return NewTemplateMap.get(getHypervisorArchKey(hypervisorType, arch)); + } + + public VMTemplateVO getRegisteredTemplate(String templateName, CPU.CPUArch arch) { + return vmTemplateDao.findLatestTemplateByName(templateName, arch); + } + + private static boolean isRunningInTest() { + return "true".equalsIgnoreCase(System.getProperty("test.mode")); + } + + /** + * Attempts to determine the templates directory path by locating the metadata file. + *

+ * This method checks if the application is running in a test environment by invoking + * {@code isRunningInTest()}. If so, it immediately returns the {@code RELATIVE_TEMPLATE_PATH}. + *

+ *

+ * Otherwise, it creates a list of candidate paths (typically including both relative and absolute + * template paths) and iterates through them. For each candidate, it constructs the metadata file + * path by appending {@code METADATA_FILE_NAME} to {@code RELATIVE_TEMPLATE_PATH} (note: the candidate + * path is not used in the file path construction in this implementation) and checks if that file exists. + * If the metadata file exists, the candidate path is returned. + *

+ *

+ * If none of the candidate paths contain the metadata file, the method logs an error and throws a + * {@link CloudRuntimeException}. + *

+ * + * @return the path to the templates directory if the metadata file is found, or {@code RELATIVE_TEMPLATE_PATH} + * when running in a test environment. + * @throws CloudRuntimeException if the metadata file cannot be located in any of the candidate paths. + */ + private static String fetchTemplatesPath() { + if (isRunningInTest()) { + return RELATIVE_TEMPLATE_PATH; + } + List paths = Arrays.asList(RELATIVE_TEMPLATE_PATH, ABSOLUTE_TEMPLATE_PATH); + for (String path : paths) { + String filePath = path + METADATA_FILE_NAME; + LOGGER.debug("Looking for file [ {} ] in the classpath.", filePath); + File metaFile = new File(filePath); + if (metaFile.exists()) { + return path; } } - return templatePath; - } - - private String getHypervisorName(String name) { - if (name.equals("xenserver")) { - return "xen"; - } - if (name.equals("ovm3")) { - return "ovm"; - } - return name; - - } - - private Hypervisor.HypervisorType getHypervisorType(String hypervisor) { - if (hypervisor.equalsIgnoreCase("xen")) { - hypervisor = "xenserver"; - } else if (hypervisor.equalsIgnoreCase("ovm")) { - hypervisor = "ovm3"; - } - return Hypervisor.HypervisorType.getType(hypervisor); + String errMsg = String.format("Unable to locate metadata file in your setup at %s", StringUtils.join(paths)); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); } private List getEligibleZoneIds() { @@ -430,28 +471,28 @@ public class SystemVmTemplateRegistration { return zoneIds; } - private Pair getNfsStoreInZone(Long zoneId) { - String url = null; - Long storeId = null; + protected Pair getNfsStoreInZone(Long zoneId) { ImageStoreVO storeVO = imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs"); if (storeVO == null) { - String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", zoneId); + String errMsg = String.format("Failed to fetch NFS store in zone = %s for SystemVM template registration", + zoneId); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - url = storeVO.getUrl(); - storeId = storeVO.getId(); + String url = storeVO.getUrl(); + Long storeId = storeVO.getId(); return new Pair<>(url, storeId); } public static void mountStore(String storeUrl, String path, String nfsVersion) { try { - if (storeUrl != null) { - URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); - String host = uri.getHost(); - String mountPath = uri.getPath(); - Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path)); + if (storeUrl == null) { + return; } + URI uri = new URI(UriUtils.encodeURIComponent(storeUrl)); + String host = uri.getHost(); + String mountPath = uri.getPath(); + Script.runSimpleBashScript(getMountCommand(nfsVersion, host + ":" + mountPath, path)); } catch (Exception e) { String msg = "NFS Store URL is not in the correct format"; LOGGER.error(msg, e); @@ -459,13 +500,6 @@ public class SystemVmTemplateRegistration { } } - private List fetchAllHypervisors(Long zoneId) { - List hypervisorList = new ArrayList<>(); - List hypervisorTypes = clusterDao.getAvailableHypervisorInZone(zoneId); - hypervisorList = hypervisorTypes.stream().distinct().map(Hypervisor.HypervisorType::name).collect(Collectors.toList()); - return hypervisorList; - } - private VMTemplateVO createTemplateObjectInDB(SystemVMTemplateDetails details) { Long templateId = vmTemplateDao.getNextInSequence(Long.class, "id"); VMTemplateVO template = new VMTemplateVO(); @@ -486,6 +520,7 @@ public class SystemVmTemplateRegistration { template.setGuestOSId(details.getGuestOsId()); template.setCrossZones(true); template.setHypervisorType(details.getHypervisorType()); + template.setArch(details.getArch()); template.setState(VirtualMachineTemplate.State.Inactive); template.setDeployAsIs(false); template = vmTemplateDao.persist(template); @@ -564,6 +599,23 @@ public class SystemVmTemplateRegistration { vmInstanceDao.updateSystemVmTemplateId(templateId, hypervisorType); } + private void updateSystemVmTemplateGuestOsId() { + String systemVmGuestOsName = "Debian GNU/Linux 12 (64-bit)"; // default + try { + GuestOSVO guestOS = guestOSDao.findOneByDisplayName(systemVmGuestOsName); + if (guestOS != null) { + LOGGER.debug("Updating SystemVM Template Guest OS [{}] id", systemVmGuestOsName); + SystemVmTemplateRegistration.LINUX_12_ID = Math.toIntExact(guestOS.getId()); + hypervisorGuestOsMap.put(Hypervisor.HypervisorType.KVM, LINUX_12_ID); + hypervisorGuestOsMap.put(Hypervisor.HypervisorType.Hyperv, LINUX_12_ID); + hypervisorGuestOsMap.put(Hypervisor.HypervisorType.LXC, LINUX_12_ID); + hypervisorGuestOsMap.put(Hypervisor.HypervisorType.Ovm3, LINUX_12_ID); + } + } catch (Exception e) { + LOGGER.warn("Couldn't update SystemVM Template Guest OS id, due to {}", e.getMessage()); + } + } + public void updateConfigurationParams(Map configParams) { for (Map.Entry config : configParams.entrySet()) { boolean updated = configurationDao.update(config.getKey(), config.getValue()); @@ -627,16 +679,20 @@ public class SystemVmTemplateRegistration { } } - private void setupTemplate(String templateName, Pair hypervisorAndTemplateName, - String destTempFolder) throws CloudRuntimeException { + private void setupTemplate(String templateName, Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, + String destTempFolder) throws CloudRuntimeException { String setupTmpltScript = Script.findScript(storageScriptsDir, "setup-sysvm-tmplt"); if (setupTmpltScript == null) { throw new CloudRuntimeException("Unable to find the createtmplt.sh"); } Script scr = new Script(setupTmpltScript, SCRIPT_TIMEOUT, LOGGER); scr.add("-u", templateName); - scr.add("-f", TEMPLATES_PATH + FileNames.get(hypervisorAndTemplateName.first())); - scr.add("-h", hypervisorAndTemplateName.first().name().toLowerCase(Locale.ROOT)); + MetadataTemplateDetails templateDetails = NewTemplateMap.get(getHypervisorArchKey(hypervisor, arch)); + String filePath = StringUtils.isNotBlank(templateDetails.getDownloadedFilePath()) ? + templateDetails.getDownloadedFilePath() : + templateDetails.getDefaultFilePath(); + scr.add("-f", filePath); + scr.add("-h", hypervisor.name().toLowerCase(Locale.ROOT)); scr.add("-d", destTempFolder); String result = scr.execute(); if (result != null) { @@ -644,17 +700,15 @@ public class SystemVmTemplateRegistration { LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - } - private Long performTemplateRegistrationOperations(Pair hypervisorAndTemplateName, - String url, String checksum, ImageFormat format, long guestOsId, - Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); + private Long performTemplateRegistrationOperations(Hypervisor.HypervisorType hypervisor, + String name, CPU.CPUArch arch, String url, String checksum, ImageFormat format, long guestOsId, + Long storeId, Long templateId, String filePath, TemplateDataStoreVO templateDataStoreVO) { String templateName = UUID.randomUUID().toString(); Date created = new Date(DateUtil.currentGMTTime().getTime()); - SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, hypervisorAndTemplateName.second(), created, - url, checksum, format, (int) guestOsId, hypervisor, storeId); + SystemVMTemplateDetails details = new SystemVMTemplateDetails(templateName, name, created, + url, checksum, format, (int) guestOsId, hypervisor, arch, storeId); if (templateId == null) { VMTemplateVO template = createTemplateObjectInDB(details); if (template == null) { @@ -671,23 +725,44 @@ public class SystemVmTemplateRegistration { if (templateDataStoreVO == null) { createTemplateStoreRefEntry(details); } - setupTemplate(templateName, hypervisorAndTemplateName, destTempFolder); + setupTemplate(templateName, hypervisor, arch, destTempFolder); readTemplateProperties(destTempFolder + "/template.properties", details); details.setUpdated(new Date(DateUtil.currentGMTTime().getTime())); updateTemplateDetails(details); return templateId; } - public void registerTemplate(Pair hypervisorAndTemplateName, - Pair storeUrlAndId, VMTemplateVO templateVO, - TemplateDataStoreVO templateDataStoreVO, String filePath) { + public void registerTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId, + VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) { + try { + performTemplateRegistrationOperations(hypervisor, name, templateVO.getArch(), templateVO.getUrl(), + templateVO.getChecksum(), templateVO.getFormat(), templateVO.getGuestOSId(), storeId, + templateVO.getId(), filePath, templateDataStoreVO); + } catch (Exception e) { + String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor); + LOGGER.error(errMsg, e); + updateTemplateTablesOnFailure(templateVO.getId()); + cleanupStore(templateVO.getId(), filePath); + throw new CloudRuntimeException(errMsg, e); + } + } + + public void registerTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch, + String name, Pair storeUrlAndId, String filePath) { Long templateId = null; try { - templateId = templateVO.getId(); - performTemplateRegistrationOperations(hypervisorAndTemplateName, templateVO.getUrl(), templateVO.getChecksum(), - templateVO.getFormat(), templateVO.getGuestOSId(), storeUrlAndId.second(), templateId, filePath, templateDataStoreVO); + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch); + templateId = performTemplateRegistrationOperations(hypervisor, name, + templateDetails.getArch(), templateDetails.getUrl(), + templateDetails.getChecksum(), hypervisorImageFormat.get(hypervisor), + hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null); + Map configParams = new HashMap<>(); + configParams.put(RouterTemplateConfigurationNames.get(hypervisor), templateDetails.getName()); + configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); + updateConfigurationParams(configParams); + updateSystemVMEntries(templateId, hypervisor); } catch (Exception e) { - String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first()); + String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisor); LOGGER.error(errMsg, e); if (templateId != null) { updateTemplateTablesOnFailure(templateId); @@ -697,26 +772,31 @@ public class SystemVmTemplateRegistration { } } - public void registerTemplate(Pair hypervisorAndTemplateName, Pair storeUrlAndId, String filePath) { - Long templateId = null; - try { - Hypervisor.HypervisorType hypervisor = hypervisorAndTemplateName.first(); - templateId = performTemplateRegistrationOperations(hypervisorAndTemplateName, NewTemplateUrl.get(hypervisor), NewTemplateChecksum.get(hypervisor), - hypervisorImageFormat.get(hypervisor), hypervisorGuestOsMap.get(hypervisor), storeUrlAndId.second(), null, filePath, null); - Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second()); - configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); - updateConfigurationParams(configParams); - updateSystemVMEntries(templateId, hypervisorAndTemplateName.first()); - } catch (Exception e) { - String errMsg = String.format("Failed to register template for hypervisor: %s", hypervisorAndTemplateName.first()); - LOGGER.error(errMsg, e); - if (templateId != null) { - updateTemplateTablesOnFailure(templateId); - cleanupStore(templateId, filePath); - } - throw new CloudRuntimeException(errMsg, e); + protected void validateTemplateFileForHypervisorAndArch(Hypervisor.HypervisorType hypervisor, CPU.CPUArch arch) { + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisor, arch); + File templateFile = getTemplateFile(templateDetails); + if (templateFile == null) { + throw new CloudRuntimeException("Failed to find local template file"); } + if (isTemplateFileChecksumDifferent(templateDetails, templateFile)) { + throw new CloudRuntimeException("Checksum failed for local template file"); + } + } + + public void validateAndRegisterTemplate(Hypervisor.HypervisorType hypervisor, String name, Long storeId, + VMTemplateVO templateVO, TemplateDataStoreVO templateDataStoreVO, String filePath) { + validateTemplateFileForHypervisorAndArch(hypervisor, templateVO.getArch()); + registerTemplate(hypervisor, name, storeId, templateVO, templateDataStoreVO, filePath); + } + + public void validateAndRegisterTemplateForNonExistingEntries(Hypervisor.HypervisorType hypervisor, + CPU.CPUArch arch, String name, Pair storeUrlAndId, String filePath) { + validateTemplateFileForHypervisorAndArch(hypervisor, arch); + registerTemplateForNonExistingEntries(hypervisor, arch, name, storeUrlAndId, filePath); + } + + protected static String getMetadataFilePath() { + return METADATA_FILE; } /** @@ -726,26 +806,42 @@ public class SystemVmTemplateRegistration { * exist a template corresponding to the current code version. */ public static String parseMetadataFile() { - try { - Ini ini = new Ini(); - ini.load(new FileReader(METADATA_FILE)); - for (Hypervisor.HypervisorType hypervisorType : hypervisorList) { - String hypervisor = hypervisorType.name().toLowerCase(Locale.ROOT); - Ini.Section section = ini.get(hypervisor); - NewTemplateNameList.put(hypervisorType, section.get("templatename")); - FileNames.put(hypervisorType, section.get("filename")); - 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); + String metadataFilePath = getMetadataFilePath(); + String errMsg = String.format("Failed to parse systemVM template metadata file: %s", metadataFilePath); + final Ini ini = new Ini(); + try (FileReader reader = new FileReader(metadataFilePath)) { + ini.load(reader); + } catch (IOException e) { LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } + if (!ini.containsKey("default")) { + errMsg = String.format("%s as unable to default section", errMsg); + LOGGER.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + for (Pair hypervisorType : hypervisorList) { + String key = getHypervisorArchKey(hypervisorType.first(), hypervisorType.second()); + Ini.Section section = ini.get(key); + if (section == null) { + LOGGER.error("Failed to find details for {} in template metadata file: {}", + key, metadataFilePath); + continue; + } + NewTemplateMap.put(key, new MetadataTemplateDetails( + hypervisorType.first(), + section.get("templatename"), + section.get("filename"), + section.get("downloadurl"), + section.get("checksum"), + hypervisorType.second(), + section.get("guestos"))); + } + Ini.Section defaultSection = ini.get("default"); + return defaultSection.get("version").trim(); } + private static void cleanupStore(Long templateId, String filePath) { String destTempFolder = filePath + PARTIAL_TEMPLATE_FOLDER + String.valueOf(templateId); try { @@ -755,31 +851,60 @@ public class SystemVmTemplateRegistration { } } - private void validateTemplates(Set hypervisorsInUse) { - Set hypervisors = hypervisorsInUse.stream(). - map(Hypervisor.HypervisorType::name).map(name -> name.toLowerCase(Locale.ROOT)).map(this::getHypervisorName).collect(Collectors.toSet()); - List templates = new ArrayList<>(); - for (Hypervisor.HypervisorType hypervisorType : hypervisorsInUse) { - templates.add(FileNames.get(hypervisorType)); + protected File getTemplateFile(MetadataTemplateDetails templateDetails) { + File templateFile = new File(templateDetails.getDefaultFilePath()); + if (templateFile.exists()) { + return templateFile; } + LOGGER.debug("{} is not present", templateFile.getAbsolutePath()); + if (DOWNLOADABLE_TEMPLATE_ARCH_TYPES.contains(templateDetails.getArch()) && + StringUtils.isNotBlank(templateDetails.getUrl())) { + LOGGER.debug("Downloading the template file {} for {}", + templateDetails.getUrl(), templateDetails.getHypervisorArchLog()); + Path path = Path.of(TEMPLATES_PATH); + if (!Files.isWritable(path)) { + templateFile = new File(tempDownloadDir, templateDetails.getFilename()); + } + if (!templateFile.exists() && + !HttpUtils.downloadFileWithProgress(templateDetails.getUrl(), templateFile.getAbsolutePath(), + LOGGER)) { + return null; + } + templateDetails.setDownloadedFilePath(templateFile.getAbsolutePath()); + } + return templateFile; + } + protected boolean isTemplateFileChecksumDifferent(MetadataTemplateDetails templateDetails, File templateFile) { + String templateChecksum = DigestHelper.calculateChecksum(templateFile); + if (!templateChecksum.equals(templateDetails.getChecksum())) { + LOGGER.error("Checksum {} for file {} does not match checksum {} from metadata", + templateChecksum, templateFile, templateDetails.getChecksum()); + return true; + } + return false; + } + + protected void validateTemplates(List> hypervisorsArchInUse) { boolean templatesFound = true; - for (String hypervisor : hypervisors) { - String matchedTemplate = templates.stream().filter(x -> x.contains(hypervisor)).findAny().orElse(null); + for (Pair hypervisorArch : hypervisorsArchInUse) { + MetadataTemplateDetails matchedTemplate = getMetadataTemplateDetails(hypervisorArch.first(), + hypervisorArch.second()); if (matchedTemplate == null) { templatesFound = false; break; } - - File tempFile = new File(TEMPLATES_PATH + matchedTemplate); - String templateChecksum = DigestHelper.calculateChecksum(tempFile); - if (!templateChecksum.equals(NewTemplateChecksum.get(getHypervisorType(hypervisor)))) { - LOGGER.error(String.format("Checksum mismatch: %s != %s ", templateChecksum, NewTemplateChecksum.get(getHypervisorType(hypervisor)))); + File tempFile = getTemplateFile(matchedTemplate); + if (tempFile == null) { + LOGGER.warn("Failed to download template for {}, moving ahead", + matchedTemplate.getHypervisorArchLog()); + continue; + } + if (isTemplateFileChecksumDifferent(matchedTemplate, tempFile)) { templatesFound = false; break; } } - if (!templatesFound) { String errMsg = "SystemVm template not found. Cannot upgrade system Vms"; LOGGER.error(errMsg); @@ -787,7 +912,40 @@ public class SystemVmTemplateRegistration { } } - public void registerTemplates(Set hypervisorsInUse) { + protected void registerTemplatesForZone(long zoneId, String filePath) { + Pair storeUrlAndId = getNfsStoreInZone(zoneId); + String nfsVersion = getNfsVersion(storeUrlAndId.second()); + mountStore(storeUrlAndId.first(), filePath, nfsVersion); + List> hypervisorArchList = + clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + for (Pair hypervisorArch : hypervisorArchList) { + Hypervisor.HypervisorType hypervisorType = hypervisorArch.first(); + MetadataTemplateDetails templateDetails = getMetadataTemplateDetails(hypervisorType, + hypervisorArch.second()); + if (templateDetails == null) { + continue; + } + VMTemplateVO templateVO = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch()); + if (templateVO != null) { + TemplateDataStoreVO templateDataStoreVO = + templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateVO.getId()); + if (templateDataStoreVO != null) { + String installPath = templateDataStoreVO.getInstallPath(); + if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) { + continue; + } + } + registerTemplate(hypervisorType, templateDetails.getName(), storeUrlAndId.second(), templateVO, + templateDataStoreVO, filePath); + updateRegisteredTemplateDetails(templateVO.getId(), templateDetails); + continue; + } + registerTemplateForNonExistingEntries(hypervisorType, templateDetails.getArch(), templateDetails.getName(), + storeUrlAndId, filePath); + } + } + + public void registerTemplates(List> hypervisorsArchInUse) { GlobalLock lock = GlobalLock.getInternLock("UpgradeDatabase-Lock"); try { LOGGER.info("Grabbing lock to register templates."); @@ -795,7 +953,7 @@ public class SystemVmTemplateRegistration { throw new CloudRuntimeException("Unable to acquire lock to register SystemVM template."); } try { - validateTemplates(hypervisorsInUse); + validateTemplates(hypervisorsArchInUse); // Perform Registration if templates not already registered Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -808,32 +966,7 @@ public class SystemVmTemplateRegistration { if (filePath == null) { throw new CloudRuntimeException("Failed to create temporary file path to mount the store"); } - Pair storeUrlAndId = getNfsStoreInZone(zoneId); - String nfsVersion = getNfsVersion(storeUrlAndId.second()); - mountStore(storeUrlAndId.first(), filePath, nfsVersion); - List hypervisorList = fetchAllHypervisors(zoneId); - for (String hypervisor : hypervisorList) { - Hypervisor.HypervisorType name = Hypervisor.HypervisorType.getType(hypervisor); - String templateName = NewTemplateNameList.get(name); - Pair hypervisorAndTemplateName = new Pair(name, templateName); - Long templateId = getRegisteredTemplateId(hypervisorAndTemplateName); - if (templateId != null) { - VMTemplateVO templateVO = vmTemplateDao.findById(templateId); - TemplateDataStoreVO templateDataStoreVO = templateDataStoreDao.findByStoreTemplate(storeUrlAndId.second(), templateId); - if (templateDataStoreVO != null) { - String installPath = templateDataStoreVO.getInstallPath(); - if (validateIfSeeded(templateDataStoreVO, storeUrlAndId.first(), installPath, nfsVersion)) { - continue; - } - } - if (templateVO != null) { - registerTemplate(hypervisorAndTemplateName, storeUrlAndId, templateVO, templateDataStoreVO, filePath); - updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName); - continue; - } - } - registerTemplate(hypervisorAndTemplateName, storeUrlAndId, filePath); - } + registerTemplatesForZone(zoneId, filePath); unmountStore(filePath); } catch (Exception e) { unmountStore(filePath); @@ -851,82 +984,99 @@ public class SystemVmTemplateRegistration { } } - private void updateRegisteredTemplateDetails(Long templateId, Map.Entry hypervisorAndTemplateName) { - Pair entry = new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue()); - updateRegisteredTemplateDetails(templateId, entry); - } - - private void updateRegisteredTemplateDetails(Long templateId, Pair hypervisorAndTemplateName) { + private void updateRegisteredTemplateDetails(Long templateId, MetadataTemplateDetails templateDetails) { VMTemplateVO templateVO = vmTemplateDao.findById(templateId); templateVO.setTemplateType(Storage.TemplateType.SYSTEM); + GuestOSVO guestOS = guestOSDao.findOneByDisplayName(templateDetails.getGuestOs()); + if (guestOS != null) { + templateVO.setGuestOSId(guestOS.getId()); + } 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); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } - - updateSystemVMEntries(templateId, hypervisorAndTemplateName.first()); - + Hypervisor.HypervisorType hypervisorType = templateDetails.getHypervisorType(); + updateSystemVMEntries(templateId, hypervisorType); // Change value of global configuration parameter router.template.* for the corresponding hypervisor and minreq.sysvmtemplate.version for the ACS version Map configParams = new HashMap<>(); - configParams.put(RouterTemplateConfigurationNames.get(hypervisorAndTemplateName.first()), hypervisorAndTemplateName.second()); + configParams.put(RouterTemplateConfigurationNames.get(hypervisorType), templateDetails.getName()); configParams.put("minreq.sysvmtemplate.version", getSystemVmTemplateVersion()); updateConfigurationParams(configParams); } - private void updateTemplateUrlAndChecksum(VMTemplateVO templateVO, Map.Entry hypervisorAndTemplateName) { - templateVO.setUrl(NewTemplateUrl.get(hypervisorAndTemplateName.getKey())); - templateVO.setChecksum(NewTemplateChecksum.get(hypervisorAndTemplateName.getKey())); + private void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO, MetadataTemplateDetails templateDetails) { + templateVO.setUrl(templateDetails.getUrl()); + templateVO.setChecksum(templateDetails.getChecksum()); + GuestOSVO guestOS = guestOSDao.findOneByDisplayName(templateDetails.getGuestOs()); + if (guestOS != null) { + templateVO.setGuestOSId(guestOS.getId()); + } boolean updated = vmTemplateDao.update(templateVO.getId(), templateVO); if (!updated) { - String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", hypervisorAndTemplateName.getKey().name()); + String errMsg = String.format("updateSystemVmTemplates:Exception while updating 'url' and 'checksum' for hypervisor type %s", templateDetails.getHypervisorType()); LOGGER.error(errMsg); throw new CloudRuntimeException(errMsg); } } + protected boolean registerOrUpdateSystemVmTemplate(MetadataTemplateDetails templateDetails, + List> hypervisorsInUse) { + LOGGER.debug("Updating System VM template for {}", templateDetails.getHypervisorArchLog()); + VMTemplateVO registeredTemplate = getRegisteredTemplate(templateDetails.getName(), templateDetails.getArch()); + // change template type to SYSTEM + if (registeredTemplate != null) { + updateRegisteredTemplateDetails(registeredTemplate.getId(), templateDetails); + } else { + boolean isHypervisorArchMatchMetadata = hypervisorsInUse.stream() + .anyMatch(p -> p.first().equals(templateDetails.getHypervisorType()) + && Objects.equals(p.second(), templateDetails.getArch())); + if (isHypervisorArchMatchMetadata) { + try { + registerTemplates(hypervisorsInUse); + return true; + } catch (final Exception e) { + throw new CloudRuntimeException(String.format("Failed to register %s templates for hypervisors: [%s]. " + + "Cannot upgrade system VMs", + getSystemVmTemplateVersion(), + StringUtils.join(hypervisorsInUse.stream() + .map(x -> getHypervisorArchKey(x.first(), x.second())) + .collect(Collectors.toList()), ",")), e); + } + } else { + LOGGER.warn("Cannot upgrade {} system VM template for {} as it is not used, not failing upgrade", + getSystemVmTemplateVersion(), templateDetails.getHypervisorArchLog()); + VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisorAndArch( + templateDetails.getHypervisorType(), templateDetails.getArch(), Storage.TemplateType.SYSTEM); + if (templateVO != null) { + updateTemplateUrlChecksumAndGuestOsId(templateVO, templateDetails); + } + } + } + return false; + } + public void updateSystemVmTemplates(final Connection conn) { LOGGER.debug("Updating System Vm template IDs"); + updateSystemVmTemplateGuestOsId(); Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { - Set hypervisorsListInUse = new HashSet(); + List> hypervisorsInUse; try { - hypervisorsListInUse = clusterDao.getDistinctAvailableHypervisorsAcrossClusters(); - + hypervisorsInUse = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(null); } catch (final Exception e) { - LOGGER.error("updateSystemVmTemplates: Exception caught while getting hypervisor types from clusters: " + e.getMessage()); - throw new CloudRuntimeException("updateSystemVmTemplates:Exception while getting hypervisor types from clusters", e); + throw new CloudRuntimeException("Exception while getting hypervisor types from clusters", e); } - - for (final Map.Entry hypervisorAndTemplateName : NewTemplateNameList.entrySet()) { - LOGGER.debug("Updating " + hypervisorAndTemplateName.getKey() + " System Vms"); - Long templateId = getRegisteredTemplateId(new Pair<>(hypervisorAndTemplateName.getKey(), hypervisorAndTemplateName.getValue())); + Collection templateEntries = NewTemplateMap.values(); + for (MetadataTemplateDetails templateDetails : templateEntries) { try { - // change template type to SYSTEM - if (templateId != null) { - updateRegisteredTemplateDetails(templateId, hypervisorAndTemplateName); - } else { - if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())) { - try { - registerTemplates(hypervisorsListInUse); - break; - } catch (final Exception e) { - throw new CloudRuntimeException(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms", getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - } - } else { - LOGGER.warn(String.format("%s %s SystemVm template not found. Cannot upgrade system Vms hypervisor is not used, so not failing upgrade", - getSystemVmTemplateVersion(), hypervisorAndTemplateName.getKey())); - // Update the latest template URLs for corresponding hypervisor - VMTemplateVO templateVO = vmTemplateDao.findLatestTemplateByTypeAndHypervisor(hypervisorAndTemplateName.getKey(), Storage.TemplateType.SYSTEM); - if (templateVO != null) { - updateTemplateUrlAndChecksum(templateVO, hypervisorAndTemplateName); - } - } + if (registerOrUpdateSystemVmTemplate(templateDetails, hypervisorsInUse)) { + break; } } catch (final Exception e) { - String errMsg = "updateSystemVmTemplates:Exception while getting ids of templates"; + String errMsg = "Exception while registering/updating system VM templates for hypervisors in metadata"; LOGGER.error(errMsg, e); throw new CloudRuntimeException(errMsg, e); } @@ -948,4 +1098,70 @@ public class SystemVmTemplateRegistration { } return null; } + + protected static class MetadataTemplateDetails { + private final Hypervisor.HypervisorType hypervisorType; + private final String name; + private final String filename; + private final String url; + private final String checksum; + private final CPU.CPUArch arch; + private String downloadedFilePath; + private final String guestOs; + + MetadataTemplateDetails(Hypervisor.HypervisorType hypervisorType, String name, String filename, String url, + String checksum, CPU.CPUArch arch, String guestOs) { + this.hypervisorType = hypervisorType; + this.name = name; + this.filename = filename; + this.url = url; + this.checksum = checksum; + this.arch = arch; + this.guestOs = guestOs; + } + + public Hypervisor.HypervisorType getHypervisorType() { + return hypervisorType; + } + + public String getName() { + return name; + } + + public String getFilename() { + return filename; + } + + public String getUrl() { + return url; + } + + public String getChecksum() { + return checksum; + } + + public CPU.CPUArch getArch() { + return arch; + } + + public String getGuestOs() { + return guestOs; + } + + public String getDownloadedFilePath() { + return downloadedFilePath; + } + + public void setDownloadedFilePath(String downloadedFilePath) { + this.downloadedFilePath = downloadedFilePath; + } + + public String getDefaultFilePath() { + return TEMPLATES_PATH + filename; + } + + public String getHypervisorArchLog() { + return SystemVmTemplateRegistration.getHypervisorArchLog(hypervisorType, arch); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java index 02c401c8155..fa0d0506ac1 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgrade.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.upgrade.dao; +import com.cloud.utils.exception.CloudRuntimeException; + import java.io.InputStream; import java.sql.Connection; @@ -24,18 +26,45 @@ public interface DbUpgrade { String getUpgradedVersion(); - boolean supportsRollingUpgrade(); + default boolean supportsRollingUpgrade() { + return false; + } /** * @return the script to prepare the database schema for the * data migration step. */ - InputStream[] getPrepareScripts(); + default InputStream[] getPrepareScripts() { + String fromVersion = getUpgradableVersionRange()[0]; + String toVersion = getUpgradableVersionRange()[1]; + final String scriptFile = String.format("META-INF/db/schema-%sto%s.sql", fromVersion.replace(".", ""), toVersion.replace(".", "")); + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } /** * Performs the actual data migration. */ - void performDataMigration(Connection conn); + default void performDataMigration(Connection conn) { + } - InputStream[] getCleanupScripts(); + default InputStream[] getCleanupScripts() { + String fromVersion = getUpgradableVersionRange()[0]; + String toVersion = getUpgradableVersionRange()[1]; + final String scriptFile = String.format("META-INF/db/schema-%sto%s-cleanup.sql", fromVersion.replace(".", ""), toVersion.replace(".", "")); + final InputStream script = Thread.currentThread().getContextClassLoader().getResourceAsStream(scriptFile); + if (script == null) { + throw new CloudRuntimeException("Unable to find " + scriptFile); + } + + return new InputStream[]{script}; + } + + default boolean refreshPoolConnectionsAfterUpgrade() { + return false; + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java index 4211898adc7..a8d2436672e 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/DbUpgradeSystemVmTemplate.java @@ -17,9 +17,23 @@ package com.cloud.upgrade.dao; +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.sql.Connection; public interface DbUpgradeSystemVmTemplate { - void updateSystemVmTemplates(Connection conn); + default void updateSystemVmTemplates(Connection conn) { + Logger logger = LogManager.getLogger(getClass()); + logger.debug("Updating System Vm template IDs"); + try { + SystemVmTemplateRegistration systemVmTemplateRegistration = new SystemVmTemplateRegistration(""); + systemVmTemplateRegistration.updateSystemVmTemplates(conn); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java index 6298e0e729a..0c0a9f070ba 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42000to42010.java @@ -78,7 +78,7 @@ public class Upgrade42000to42010 extends DbUpgradeAbstractImpl implements DbUpgr try { systemVmTemplateRegistration.updateSystemVmTemplates(conn); } catch (Exception e) { - throw new CloudRuntimeException("Failed to find / register SystemVM template(s)"); + throw new CloudRuntimeException("Failed to find / register SystemVM template(s)", e); } } @@ -100,8 +100,6 @@ public class Upgrade42000to42010 extends DbUpgradeAbstractImpl implements DbUpgr DbUpgradeUtils.addIndexIfNeeded(conn, "network_offering_details", "name"); - DbUpgradeUtils.addIndexIfNeeded(conn, "network_offering_details", "resource_id", "resource_type"); - DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "cpu"); DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "speed"); DbUpgradeUtils.addIndexIfNeeded(conn, "service_offering", "ram_size"); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java index d6dc85dbb9a..786ee5afbc8 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42010to42100.java @@ -16,18 +16,22 @@ // under the License. package com.cloud.upgrade.dao; -import com.cloud.upgrade.SystemVmTemplateRegistration; -import com.cloud.utils.db.TransactionLegacy; -import com.cloud.utils.exception.CloudRuntimeException; - import java.io.InputStream; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.apache.cloudstack.framework.config.ConfigKey; +import com.cloud.upgrade.SystemVmTemplateRegistration; +import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.exception.CloudRuntimeException; + public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { private SystemVmTemplateRegistration systemVmTemplateRegistration; @@ -59,6 +63,7 @@ public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgr @Override public void performDataMigration(Connection conn) { + updateKubernetesClusterNodeVersions(conn); migrateConfigurationScopeToBitmask(conn); } @@ -88,6 +93,95 @@ public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgr } } + private void updateKubernetesClusterNodeVersions(Connection conn) { + //get list of all non removed kubernetes clusters + try { + Map clusterAndVersion = getKubernetesClusterIdsAndVersion(conn); + updateKubernetesNodeVersions(conn, clusterAndVersion); + } catch (Exception e) { + String errMsg = "Failed to update kubernetes cluster nodes version"; + logger.error(errMsg); + throw new CloudRuntimeException(errMsg, e); + } + } + + private Map getKubernetesClusterIdsAndVersion(Connection conn) { + String listKubernetesClusters = "SELECT c.id, v.semantic_version FROM `cloud`.`kubernetes_cluster` c JOIN `cloud`.`kubernetes_supported_version` v ON (c.kubernetes_version_id = v.id) WHERE c.removed is NULL;"; + Map clusterAndVersion = new HashMap<>(); + try { + PreparedStatement pstmt = conn.prepareStatement(listKubernetesClusters); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + clusterAndVersion.put(rs.getLong(1), rs.getString(2)); + } + rs.close(); + pstmt.close(); + } catch (SQLException e) { + String errMsg = String.format("Failed to get all the kubernetes cluster ids due to: %s", e.getMessage()); + logger.error(errMsg); + throw new CloudRuntimeException(errMsg, e); + } + return clusterAndVersion; + } + + private void updateKubernetesNodeVersions(Connection conn, Map clusterAndVersion) { + List kubernetesClusterVmIds; + for (Map.Entry clusterVersionEntry : clusterAndVersion.entrySet()) { + try { + Long cksClusterId = clusterVersionEntry.getKey(); + String cksVersion = clusterVersionEntry.getValue(); + logger.debug(String.format("Adding CKS version %s to existing CKS cluster %s nodes", cksVersion, cksClusterId)); + kubernetesClusterVmIds = getKubernetesClusterVmMapIds(conn, cksClusterId); + updateKubernetesNodeVersion(conn, kubernetesClusterVmIds, cksClusterId, cksVersion); + } catch (Exception e) { + String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" + + " kubernetes cluster with id: %s," + + " due to: %s", clusterVersionEntry.getKey(), e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + } + } + + private List getKubernetesClusterVmMapIds(Connection conn, Long cksClusterId) { + List kubernetesClusterVmIds = new ArrayList<>(); + String getKubernetesClustersVmMap = "SELECT id FROM `cloud`.`kubernetes_cluster_vm_map` WHERE cluster_id = %s;"; + try { + PreparedStatement pstmt = conn.prepareStatement(String.format(getKubernetesClustersVmMap, cksClusterId)); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + kubernetesClusterVmIds.add(rs.getLong(1)); + } + rs.close(); + pstmt.close(); + } catch (SQLException e) { + String errMsg = String.format("Failed to get the kubernetes cluster vm map IDs for kubernetes cluster with id: %s," + + " due to: %s", cksClusterId, e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + return kubernetesClusterVmIds; + } + + private void updateKubernetesNodeVersion(Connection conn, List kubernetesClusterVmIds, Long cksClusterId, String cksVersion) { + String updateKubernetesNodeVersion = "UPDATE `cloud`.`kubernetes_cluster_vm_map` set kubernetes_node_version = ? WHERE id = ?;"; + for (Long nodeVmId : kubernetesClusterVmIds) { + try { + PreparedStatement pstmt = conn.prepareStatement(updateKubernetesNodeVersion); + pstmt.setString(1, cksVersion); + pstmt.setLong(2, nodeVmId); + pstmt.executeUpdate(); + pstmt.close(); + } catch (Exception e) { + String errMsg = String.format("Failed to update the node version for kubernetes cluster nodes for the" + + " kubernetes cluster with id: %s," + + " due to: %s", cksClusterId, e.getMessage()); + logger.error(errMsg, e); + throw new CloudRuntimeException(errMsg, e); + } + } + } + protected void migrateConfigurationScopeToBitmask(Connection conn) { String scopeDataType = DbUpgradeUtils.getTableColumnType(conn, "configuration", "scope"); logger.info("Data type of the column scope of table configuration is {}", scopeDataType); @@ -118,4 +212,9 @@ public class Upgrade42010to42100 extends DbUpgradeAbstractImpl implements DbUpgr throw new CloudRuntimeException(String.format("Failed to migrate existing configuration scope values to bitmask due to: %s", e.getMessage())); } } + + @Override + public boolean refreshPoolConnectionsAfterUpgrade() { + return true; + } } diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java new file mode 100644 index 00000000000..c2cfd02c15c --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42100to42200.java @@ -0,0 +1,30 @@ +// 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.dao; + +public class Upgrade42100to42200 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate { + + @Override + public String[] getUpgradableVersionRange() { + return new String[]{"4.21.0.0", "4.22.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "4.22.0.0"; + } +} diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java index 8a72182ec67..2a24016653d 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDao.java @@ -24,7 +24,7 @@ import com.cloud.usage.UsageBackupVO; import com.cloud.utils.db.GenericDao; public interface UsageBackupDao extends GenericDao { - void updateMetrics(Long vmId, Long size, Long virtualSize); - void removeUsage(Long accountId, Long vmId, Date eventDate); + void updateMetrics(Long vmId, Long backupOfferingId, Long size, Long virtualSize); + void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate); List getUsageRecords(Long accountId, Date startDate, Date endDate); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java index 3403a8dfe5b..e5b46b02a59 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageBackupDaoImpl.java @@ -36,16 +36,17 @@ import com.cloud.utils.db.TransactionLegacy; @Component public class UsageBackupDaoImpl extends GenericDaoBase implements UsageBackupDao { - protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and removed IS NULL"; + protected static final String UPDATE_DELETED = "UPDATE usage_backup SET removed = ? WHERE account_id = ? AND vm_id = ? and backup_offering_id = ? and removed IS NULL"; protected static final String GET_USAGE_RECORDS_BY_ACCOUNT = "SELECT id, zone_id, account_id, domain_id, vm_id, backup_offering_id, size, protected_size, created, removed FROM usage_backup WHERE " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " OR ((created <= ?) AND (removed >= ?)))"; @Override - public void updateMetrics(final Long vmId, final Long size, final Long virtualSize) { + public void updateMetrics(final Long vmId, Long backupOfferingId, final Long size, final Long virtualSize) { try (TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB)) { SearchCriteria sc = this.createSearchCriteria(); sc.addAnd("vmId", SearchCriteria.Op.EQ, vmId); + sc.addAnd("backupOfferingId", SearchCriteria.Op.EQ, backupOfferingId); UsageBackupVO vo = findOneBy(sc); if (vo != null) { vo.setSize(size); @@ -58,7 +59,7 @@ public class UsageBackupDaoImpl extends GenericDaoBase impl } @Override - public void removeUsage(Long accountId, Long vmId, Date eventDate) { + public void removeUsage(Long accountId, Long vmId, Long backupOfferingId, Date eventDate) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { txn.start(); @@ -67,6 +68,7 @@ public class UsageBackupDaoImpl extends GenericDaoBase impl pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), eventDate)); pstmt.setLong(2, accountId); pstmt.setLong(3, vmId); + pstmt.setLong(3, backupOfferingId); pstmt.executeUpdate(); } } catch (SQLException e) { diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java index f22a906054d..d4038d4ceeb 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDao.java @@ -37,4 +37,6 @@ public interface UsageJobDao extends GenericDao { UsageJobVO isOwner(String hostname, int pid); void updateJobSuccess(Long jobId, long startMillis, long endMillis, long execTime, boolean success); + + void removeLastOpenJobsOwned(String hostname, int pid); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java index 6d460aadd09..44a7d1a8b72 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageJobDaoImpl.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.List; +import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; import com.cloud.usage.UsageJobVO; @@ -114,7 +115,7 @@ public class UsageJobDaoImpl extends GenericDaoBase implements public UsageJobVO isOwner(String hostname, int pid) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); try { - if ((hostname == null) || (pid <= 0)) { + if (hostname == null || pid <= 0) { return null; } @@ -174,7 +175,7 @@ public class UsageJobDaoImpl extends GenericDaoBase implements SearchCriteria sc = createSearchCriteria(); sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); sc.addAnd("jobType", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_TYPE_SINGLE)); - sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(0)); + sc.addAnd("scheduled", SearchCriteria.Op.EQ, Integer.valueOf(UsageJobVO.JOB_NOT_SCHEDULED)); List jobs = search(sc, filter); if ((jobs == null) || jobs.isEmpty()) { @@ -194,4 +195,36 @@ public class UsageJobDaoImpl extends GenericDaoBase implements } return jobs.get(0).getHeartbeat(); } + + private List getLastOpenJobsOwned(String hostname, int pid) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("endMillis", SearchCriteria.Op.EQ, Long.valueOf(0)); + sc.addAnd("host", SearchCriteria.Op.EQ, hostname); + if (pid > 0) { + sc.addAnd("pid", SearchCriteria.Op.EQ, Integer.valueOf(pid)); + } + return listBy(sc); + } + + @Override + public void removeLastOpenJobsOwned(String hostname, int pid) { + if (hostname == null) { + return; + } + + TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + try { + List jobs = getLastOpenJobsOwned(hostname, pid); + if (CollectionUtils.isNotEmpty(jobs)) { + logger.info("Found {} opens job, to remove", jobs.size()); + for (UsageJobVO job : jobs) { + logger.debug("Removing job - id: {}, pid: {}, job type: {}, scheduled: {}, heartbeat: {}", + job.getId(), job.getPid(), job.getJobType(), job.getScheduled(), job.getHeartbeat()); + remove(job.getId()); + } + } + } finally { + txn.close(); + } + } } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java index 9be4c49b55e..4d84caaad44 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDao.java @@ -28,4 +28,6 @@ public interface UsageNetworksDao extends GenericDao { void remove(long networkId, Date removed); List getUsageRecords(Long accountId, Date startDate, Date endDate); + + List listAll(long networkId); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java index f8ffbf74f85..e7ae622ae54 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageNetworksDaoImpl.java @@ -16,10 +16,10 @@ // under the License. package com.cloud.usage.dao; -import com.cloud.network.Network; import com.cloud.usage.UsageNetworksVO; import com.cloud.utils.DateUtil; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; @@ -27,6 +27,7 @@ import org.springframework.stereotype.Component; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.annotation.PostConstruct; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; @@ -41,6 +42,14 @@ public class UsageNetworksDaoImpl extends GenericDaoBase " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " OR ((created <= ?) AND (removed >= ?)))"; + private SearchBuilder usageNetworksSearch; + + @PostConstruct + public void init() { + usageNetworksSearch = createSearchBuilder(); + usageNetworksSearch.and("networkId", usageNetworksSearch.entity().getNetworkId(), SearchCriteria.Op.EQ); + usageNetworksSearch.done(); + } @Override public void update(long networkId, long newNetworkOffering, String state) { @@ -70,11 +79,10 @@ public class UsageNetworksDaoImpl extends GenericDaoBase SearchCriteria sc = this.createSearchCriteria(); sc.addAnd("networkId", SearchCriteria.Op.EQ, networkId); sc.addAnd("removed", SearchCriteria.Op.NULL); - UsageNetworksVO vo = findOneBy(sc); - if (vo != null) { - vo.setRemoved(removed); - vo.setState(Network.State.Destroy.name()); - update(vo.getId(), vo); + List usageNetworksVOs = listBy(sc); + for (UsageNetworksVO entry : usageNetworksVOs) { + entry.setRemoved(removed); + update(entry.getId(), entry); } } catch (final Exception e) { txn.rollback(); @@ -133,4 +141,11 @@ public class UsageNetworksDaoImpl extends GenericDaoBase return usageRecords; } + + @Override + public List listAll(long networkId) { + SearchCriteria sc = usageNetworksSearch.create(); + sc.setParameters("networkId", networkId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java index a1514aba4ca..5167bf88c48 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDao.java @@ -24,6 +24,10 @@ import java.util.List; public interface UsageVpcDao extends GenericDao { void update(UsageVpcVO usage); + void remove(long vpcId, Date removed); + List getUsageRecords(Long accountId, Date startDate, Date endDate); + + List listAll(long vpcId); } diff --git a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java index 45ae845e58f..b5d8e46ef09 100644 --- a/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/usage/dao/UsageVpcDaoImpl.java @@ -16,14 +16,15 @@ // under the License. package com.cloud.usage.dao; -import com.cloud.network.vpc.Vpc; import com.cloud.usage.UsageVpcVO; import com.cloud.utils.DateUtil; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; @@ -37,6 +38,15 @@ public class UsageVpcDaoImpl extends GenericDaoBase implements " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " OR ((created <= ?) AND (removed >= ?)))"; + private SearchBuilder usageVpcSearch; + + @PostConstruct + public void init() { + usageVpcSearch = createSearchBuilder(); + usageVpcSearch.and("vpcId", usageVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ); + usageVpcSearch.done(); + } + @Override public void update(UsageVpcVO usage) { TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); @@ -64,11 +74,10 @@ public class UsageVpcDaoImpl extends GenericDaoBase implements SearchCriteria sc = this.createSearchCriteria(); sc.addAnd("vpcId", SearchCriteria.Op.EQ, vpcId); sc.addAnd("removed", SearchCriteria.Op.NULL); - UsageVpcVO vo = findOneBy(sc); - if (vo != null) { - vo.setRemoved(removed); - vo.setState(Vpc.State.Inactive.name()); - update(vo.getId(), vo); + List usageVpcVOs = listBy(sc); + for (UsageVpcVO entry : usageVpcVOs) { + entry.setRemoved(removed); + update(entry.getId(), entry); } } catch (final Exception e) { txn.rollback(); @@ -126,4 +135,11 @@ public class UsageVpcDaoImpl extends GenericDaoBase implements return usageRecords; } + + @Override + public List listAll(long vpcId) { + SearchCriteria sc = usageVpcSearch.create(); + sc.setParameters("vpcId", vpcId); + return listBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java index a8e48ad22b1..e8864976069 100644 --- a/engine/schema/src/main/java/com/cloud/user/UserDataVO.java +++ b/engine/schema/src/main/java/com/cloud/user/UserDataVO.java @@ -65,6 +65,9 @@ public class UserDataVO implements UserData { @Column(name = GenericDao.REMOVED_COLUMN) private Date removed; + @Column(name = "for_cks") + private boolean forCks; + @Override public long getDomainId() { return domainId; @@ -105,6 +108,11 @@ public class UserDataVO implements UserData { return params; } + @Override + public boolean isForCks() { + return forCks; + } + public void setAccountId(long accountId) { this.accountId = accountId; } @@ -132,4 +140,6 @@ public class UserDataVO implements UserData { public Date getRemoved() { return removed; } + + public void setForCks(boolean forCks) { this.forCks = forCks; } } diff --git a/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java b/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java index 17b07496731..dae5f3a3467 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/AccountDao.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.user.dao; +import java.util.Date; +import java.util.List; + import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.User; @@ -23,9 +26,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import java.util.Date; -import java.util.List; - public interface AccountDao extends GenericDao { Pair findUserAccountByApiKey(String apiKey); @@ -33,6 +33,8 @@ public interface AccountDao extends GenericDao { Pair, Integer> findAccountsLike(String accountName, Filter filter); + List findAccountsByName(String accountName); + List findActiveAccounts(Long maxAccountId, Filter filter); List findRecentlyDeletedAccounts(Long maxAccountId, Date earliestRemovedDate, Filter filter); diff --git a/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java b/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java index 2654b22374f..f5f95d5da1f 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/AccountDaoImpl.java @@ -16,6 +16,14 @@ // under the License. package com.cloud.user.dao; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + import com.cloud.user.Account; import com.cloud.user.Account.State; import com.cloud.user.AccountVO; @@ -30,14 +38,7 @@ import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; -import org.apache.commons.lang3.StringUtils; import com.cloud.utils.db.TransactionLegacy; -import org.springframework.stereotype.Component; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.util.Date; -import java.util.List; @Component public class AccountDaoImpl extends GenericDaoBase implements AccountDao { @@ -190,6 +191,16 @@ public class AccountDaoImpl extends GenericDaoBase implements A return searchAndCount(sc, filter); } + @Override + public List findAccountsByName(String accountName) { + SearchBuilder sb = createSearchBuilder(); + sb.and("accountName", sb.entity().getAccountName(), SearchCriteria.Op.EQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("accountName", accountName); + return search(sc, null); + } + @Override public Account findEnabledAccount(String accountName, Long domainId) { SearchCriteria sc = AllFieldsSearch.create("accountName", accountName); diff --git a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java index ef777be2de9..d8f2838dd47 100644 --- a/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/ConsoleSessionVO.java @@ -19,6 +19,8 @@ package com.cloud.vm; +import org.apache.cloudstack.consoleproxy.ConsoleSession; + import java.util.Date; import javax.persistence.Column; @@ -32,7 +34,7 @@ import javax.persistence.TemporalType; @Entity @Table(name = "console_session") -public class ConsoleSessionVO { +public class ConsoleSessionVO implements ConsoleSession { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -45,6 +47,9 @@ public class ConsoleSessionVO { @Column(name = "created") private Date created; + @Column(name = "domain_id") + private long domainId; + @Column(name = "account_id") private long accountId; @@ -86,6 +91,7 @@ public class ConsoleSessionVO { this.uuid = uuid; } + @Override public Date getCreated() { return created; } @@ -94,6 +100,16 @@ public class ConsoleSessionVO { this.created = created; } + @Override + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + @Override public long getAccountId() { return accountId; } @@ -102,6 +118,7 @@ public class ConsoleSessionVO { this.accountId = accountId; } + @Override public long getUserId() { return userId; } @@ -110,6 +127,7 @@ public class ConsoleSessionVO { this.userId = userId; } + @Override public long getInstanceId() { return instanceId; } @@ -118,6 +136,7 @@ public class ConsoleSessionVO { this.instanceId = instanceId; } + @Override public long getHostId() { return hostId; } @@ -126,6 +145,7 @@ public class ConsoleSessionVO { this.hostId = hostId; } + @Override public Date getRemoved() { return removed; } @@ -134,6 +154,7 @@ public class ConsoleSessionVO { this.removed = removed; } + @Override public Date getAcquired() { return acquired; } @@ -142,6 +163,7 @@ public class ConsoleSessionVO { this.acquired = acquired; } + @Override public String getConsoleEndpointCreatorAddress() { return consoleEndpointCreatorAddress; } @@ -150,6 +172,7 @@ public class ConsoleSessionVO { this.consoleEndpointCreatorAddress = consoleEndpointCreatorAddress; } + @Override public String getClientAddress() { return clientAddress; } diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java similarity index 90% rename from engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java rename to engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java index 81bb6dd9d4f..7879aa24556 100755 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmDetailVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceDetailVO.java @@ -26,8 +26,8 @@ import javax.persistence.Table; import org.apache.cloudstack.api.ResourceDetail; @Entity -@Table(name = "user_vm_details") -public class UserVmDetailVO implements ResourceDetail { +@Table(name = "vm_instance_details") +public class VMInstanceDetailVO implements ResourceDetail { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") @@ -45,10 +45,10 @@ public class UserVmDetailVO implements ResourceDetail { @Column(name = "display") private boolean display = true; - public UserVmDetailVO() { + public VMInstanceDetailVO() { } - public UserVmDetailVO(long vmId, String name, String value, boolean display) { + public VMInstanceDetailVO(long vmId, String name, String value, boolean display) { this.resourceId = vmId; this.name = name; this.value = value; diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index a1d9f4a8089..9d5e1b0ff50 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -503,7 +503,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject { void acquireSession(String sessionUuid, String clientAddress); int expungeByVmList(List vmIds, Long batchSize); + + Pair, Integer> listConsoleSessions(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired, Long pageSizeVal, Long startIndex); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java index 3d117894670..562142eecc8 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/ConsoleSessionDaoImpl.java @@ -22,6 +22,8 @@ package com.cloud.vm.dao; import java.util.Date; import java.util.List; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; import org.apache.commons.collections.CollectionUtils; import com.cloud.utils.db.GenericDaoBase; @@ -30,13 +32,28 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.vm.ConsoleSessionVO; public class ConsoleSessionDaoImpl extends GenericDaoBase implements ConsoleSessionDao { + private static final String ID = "id"; + private static final String DOMAIN_IDS = "domainIds"; + private static final String ACCOUNT_ID = "accountId"; + private static final String USER_ID = "userId"; + private static final String HOST_ID = "hostId"; + private static final String INSTANCE_ID = "instanceId"; + private static final String VM_IDS = "vmIds"; + private static final String START_DATE = "startDate"; + private static final String END_DATE = "endDate"; + private static final String CREATOR_ADDRESS = "creatorAddress"; + private static final String CLIENT_ADDRESS = "clientAddress"; + private static final String ACQUIRED = "acquired"; + private static final String CREATED = "created"; + private static final String REMOVED = "removed"; + private static final String REMOVED_NOT_NULL = "removedNotNull"; private final SearchBuilder searchByRemovedDate; public ConsoleSessionDaoImpl() { searchByRemovedDate = createSearchBuilder(); - searchByRemovedDate.and("removedNotNull", searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.NNULL); - searchByRemovedDate.and("removed", searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.LTEQ); + searchByRemovedDate.and(REMOVED_NOT_NULL, searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.NNULL); + searchByRemovedDate.and(REMOVED, searchByRemovedDate.entity().getRemoved(), SearchCriteria.Op.LTEQ); } @Override @@ -57,7 +74,7 @@ public class ConsoleSessionDaoImpl extends GenericDaoBase searchCriteria = searchByRemovedDate.create(); - searchCriteria.setParameters("removed", date); + searchCriteria.setParameters(REMOVED, date); return expunge(searchCriteria); } @@ -75,9 +92,66 @@ public class ConsoleSessionDaoImpl extends GenericDaoBase sb = createSearchBuilder(); - sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN); + sb.and(VM_IDS, sb.entity().getInstanceId(), SearchCriteria.Op.IN); SearchCriteria sc = sb.create(); - sc.setParameters("vmIds", vmIds.toArray()); + sc.setParameters(VM_IDS, vmIds.toArray()); return batchExpunge(sc, batchSize); } + + @Override + public Pair, Integer> listConsoleSessions(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired, Long pageSizeVal, Long startIndex) { + Filter filter = new Filter(ConsoleSessionVO.class, CREATED, false, startIndex, pageSizeVal); + SearchCriteria searchCriteria = createListConsoleSessionsSearchCriteria(id, domainIds, accountId, userId, hostId, + startDate, endDate, instanceId, consoleEndpointCreatorAddress, clientAddress, activeOnly, acquired); + + return searchAndCount(searchCriteria, filter, true); + } + + private SearchCriteria createListConsoleSessionsSearchCriteria(Long id, List domainIds, Long accountId, Long userId, Long hostId, + Date startDate, Date endDate, Long instanceId, + String consoleEndpointCreatorAddress, String clientAddress, + boolean activeOnly, boolean acquired) { + SearchCriteria searchCriteria = createListConsoleSessionsSearchBuilder(activeOnly, acquired).create(); + + searchCriteria.setParametersIfNotNull(ID, id); + searchCriteria.setParametersIfNotNull(DOMAIN_IDS, domainIds.toArray()); + searchCriteria.setParametersIfNotNull(ACCOUNT_ID, accountId); + searchCriteria.setParametersIfNotNull(USER_ID, userId); + searchCriteria.setParametersIfNotNull(HOST_ID, hostId); + searchCriteria.setParametersIfNotNull(INSTANCE_ID, instanceId); + searchCriteria.setParametersIfNotNull(START_DATE, startDate); + searchCriteria.setParametersIfNotNull(END_DATE, endDate); + searchCriteria.setParametersIfNotNull(CREATOR_ADDRESS, consoleEndpointCreatorAddress); + searchCriteria.setParametersIfNotNull(CLIENT_ADDRESS, clientAddress); + + return searchCriteria; + } + + private SearchBuilder createListConsoleSessionsSearchBuilder(boolean activeOnly, boolean acquired) { + SearchBuilder searchBuilder = createSearchBuilder(); + + searchBuilder.and(ID, searchBuilder.entity().getId(), SearchCriteria.Op.EQ); + searchBuilder.and(DOMAIN_IDS, searchBuilder.entity().getDomainId(), SearchCriteria.Op.IN); + searchBuilder.and(ACCOUNT_ID, searchBuilder.entity().getAccountId(), SearchCriteria.Op.EQ); + searchBuilder.and(USER_ID, searchBuilder.entity().getUserId(), SearchCriteria.Op.EQ); + searchBuilder.and(HOST_ID, searchBuilder.entity().getHostId(), SearchCriteria.Op.EQ); + searchBuilder.and(INSTANCE_ID, searchBuilder.entity().getInstanceId(), SearchCriteria.Op.EQ); + searchBuilder.and(START_DATE, searchBuilder.entity().getCreated(), SearchCriteria.Op.GTEQ); + searchBuilder.and(END_DATE, searchBuilder.entity().getCreated(), SearchCriteria.Op.LTEQ); + searchBuilder.and(CREATOR_ADDRESS, searchBuilder.entity().getConsoleEndpointCreatorAddress(), SearchCriteria.Op.EQ); + searchBuilder.and(CLIENT_ADDRESS, searchBuilder.entity().getClientAddress(), SearchCriteria.Op.EQ); + + if (activeOnly) { + searchBuilder.and(ACQUIRED, searchBuilder.entity().getAcquired(), SearchCriteria.Op.NNULL); + searchBuilder.and(REMOVED, searchBuilder.entity().getRemoved(), SearchCriteria.Op.NULL); + } else if (acquired) { + searchBuilder.and(ACQUIRED, searchBuilder.entity().getAcquired(), SearchCriteria.Op.NNULL); + } + + searchBuilder.done(); + return searchBuilder; + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java index d34b03c4cb0..70a2558e2d4 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java @@ -46,8 +46,12 @@ public interface NicDao extends GenericDao { NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType); + NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId); + NicVO findByNetworkIdAndMacAddress(long networkId, String mac); NicVO findDefaultNicForVM(long instanceId); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java index 7d1af1982ae..3618785c1b8 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -69,6 +69,7 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { AllFieldsSearch.and("secondaryip", AllFieldsSearch.entity().getSecondaryIp(), Op.EQ); AllFieldsSearch.and("nicid", AllFieldsSearch.entity().getId(), Op.EQ); AllFieldsSearch.and("strategy", AllFieldsSearch.entity().getReservationStrategy(), Op.EQ); + AllFieldsSearch.and("strategyNEQ", AllFieldsSearch.entity().getReservationStrategy(), Op.NEQ); AllFieldsSearch.and("reserverName",AllFieldsSearch.entity().getReserver(),Op.EQ); AllFieldsSearch.and("macAddress", AllFieldsSearch.entity().getMacAddress(), Op.EQ); AllFieldsSearch.and("deviceid", AllFieldsSearch.entity().getDeviceId(), Op.EQ); @@ -195,6 +196,15 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { return findByNetworkIdAndTypeInternal(networkId, vmType, true); } + @Override + public NicVO findNonPlaceHolderByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("network", networkId); + sc.setParameters("vmType", vmType); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); + return findOneBy(sc); + } + @Override public NicVO findByNetworkIdTypeAndGateway(long networkId, VirtualMachine.Type vmType, String gateway) { SearchCriteria sc = AllFieldsSearch.create(); @@ -222,6 +232,16 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { return findOneBy(sc); } + @Override + public NicVO findNonPlaceHolderByIp4AddressAndNetworkId(String ip4Address, long networkId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("address", ip4Address); + sc.setParameters("network", networkId); + sc.setParameters("strategyNEQ", Nic.ReservationStrategy.PlaceHolder.toString()); + return findOneBy(sc); + } + + @Override public NicVO findByNetworkIdAndMacAddress(long networkId, String mac) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java index cc8b9fc59a8..41bcb3155e5 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java @@ -57,7 +57,7 @@ import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.NicVO; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; @@ -124,13 +124,13 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use + "left join security_group on security_group_vm_map.security_group_id=security_group.id " + "left join nics on vm_instance.id=nics.instance_id " + "left join networks on nics.network_id=networks.id " + "left join user_ip_address on user_ip_address.vm_id=vm_instance.id " + "where vm_instance.id in ("; - private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , user_vm_details.value, user_vm_details.name from vm_instance " - + "left join user_vm_details on vm_instance.id = user_vm_details.vm_id where (user_vm_details.name is null or user_vm_details.name = ? ) and vm_instance.instance_name in ("; + private static final String VMS_DETAIL_BY_NAME = "select vm_instance.instance_name, vm_instance.vm_type, vm_instance.id , vm_instance_details.value, vm_instance_details.name from vm_instance " + + "left join vm_instance_details on vm_instance.id = vm_instance_details.vm_id where (vm_instance_details.name is null or vm_instance_details.name = ? ) and vm_instance.instance_name in ("; private static final int VM_DETAILS_BATCH_SIZE = 100; @Inject - protected UserVmDetailsDao _detailsDao; + protected VMInstanceDetailsDao _detailsDao; @Inject protected NicDao _nicDao; @@ -445,10 +445,10 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use final Map visibilityMap = _detailsDao.listDetailsVisibility(vm.getId()); - List details = new ArrayList(); + List details = new ArrayList(); for (Map.Entry entry : detailsStr.entrySet()) { boolean display = !hiddenDetails.contains(entry.getKey()) && visibilityMap.getOrDefault(entry.getKey(), true); - details.add(new UserVmDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); + details.add(new VMInstanceDetailVO(vm.getId(), entry.getKey(), entry.getValue(), display)); } _detailsDao.saveDetails(details); @@ -755,7 +755,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use while (rs.next()) { vmsDetailByNames.add(new Pair, Pair>(new Pair( rs.getString("vm_instance.instance_name"), VirtualMachine.Type.valueOf(rs.getString("vm_type"))), - new Pair(rs.getLong("vm_instance.id"), rs.getString("user_vm_details.value")))); + new Pair(rs.getLong("vm_instance.id"), rs.getString("vm_instance_details.value")))); } } } catch (SQLException e) { @@ -821,6 +821,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use reservationDao.setResourceId(Resource.ResourceType.user_vm, userVM.getId()); reservationDao.setResourceId(Resource.ResourceType.cpu, userVM.getId()); reservationDao.setResourceId(Resource.ResourceType.memory, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.gpu, userVM.getId()); return userVM; }); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java index 823642d8c3d..f722b4c54e4 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java @@ -118,7 +118,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listVmsMigratingFromHost(Long hostId); - List listByZoneWithBackups(Long zoneId, Long backupOfferingId); + List listByZoneAndBackupOffering(Long zoneId, Long backupOfferingId); public Long countActiveByHostId(long hostId); @@ -187,4 +187,5 @@ public interface VMInstanceDao extends GenericDao, StateDao< Map getNameIdMapForVmIds(Collection ids); + List listByIdsIncludingRemoved(List ids); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index ef10af63bae..2f19d36c37a 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -131,12 +131,22 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " GROUP BY host.id ORDER BY 2 ASC "; - private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY = "SELECT pci, type, SUM(vmcount) FROM (SELECT MAX(IF(offering.name = 'pciDevice',value,'')) AS pci, MAX(IF(offering.name = 'vgpuType', value,'')) " + "AS type, COUNT(DISTINCT vm.id) AS vmcount FROM service_offering_details offering INNER JOIN vm_instance vm ON offering.service_offering_id = vm.service_offering_id " + "INNER JOIN `cloud`.`host` ON vm.host_id = host.id WHERE vm.state = 'Running' AND host.data_center_id = ? "; + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY = + "GROUP BY vm.service_offering_id) results GROUP BY pci, type"; + + private static final String COUNT_VMS_BASED_ON_VGPU_TYPES1 = + "SELECT CONCAT(gpu_card.vendor_name, ' ', gpu_card.device_name), vgpu_profile.name, COUNT(gpu_device.vm_id) " + + "FROM `cloud`.`gpu_device` " + + "INNER JOIN `cloud`.`host` ON gpu_device.host_id = host.id " + + "INNER JOIN `cloud`.`gpu_card` ON gpu_device.card_id = gpu_card.id " + + "INNER JOIN `cloud`.`vgpu_profile` ON vgpu_profile.id = gpu_device.vgpu_profile_id " + + "WHERE vm_id IS NOT NULL AND host.data_center_id = ? "; private static final String COUNT_VMS_BASED_ON_VGPU_TYPES2 = - "GROUP BY offering.service_offering_id) results GROUP BY pci, type"; + "GROUP BY gpu_card.name, vgpu_profile.name"; private static final String UPDATE_SYSTEM_VM_TEMPLATE_ID_FOR_HYPERVISOR = "UPDATE `cloud`.`vm_instance` SET vm_template_id = ? WHERE type <> 'User' AND hypervisor_type = ? AND removed is NULL"; @@ -651,7 +661,7 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem } @Override - public List listByZoneWithBackups(Long zoneId, Long backupOfferingId) { + public List listByZoneAndBackupOffering(Long zoneId, Long backupOfferingId) { SearchCriteria sc = BackupSearch.create(); sc.setParameters("zone_id", zoneId); if (backupOfferingId != null) { @@ -794,40 +804,52 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem @Override public HashMap countVgpuVMs(Long dcId, Long podId, Long clusterId) { + StringBuilder finalQueryLegacy = new StringBuilder(); StringBuilder finalQuery = new StringBuilder(); TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmtLegacy = null; PreparedStatement pstmt = null; List resourceIdList = new ArrayList(); HashMap result = new HashMap(); resourceIdList.add(dcId); + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES1_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES1); if (podId != null) { + finalQueryLegacy.append("AND host.pod_id = ? "); finalQuery.append("AND host.pod_id = ? "); resourceIdList.add(podId); } if (clusterId != null) { + finalQueryLegacy.append("AND host.cluster_id = ? "); finalQuery.append("AND host.cluster_id = ? "); resourceIdList.add(clusterId); } + finalQueryLegacy.append(COUNT_VMS_BASED_ON_VGPU_TYPES2_LEGACY); finalQuery.append(COUNT_VMS_BASED_ON_VGPU_TYPES2); try { + pstmtLegacy = txn.prepareAutoCloseStatement(finalQueryLegacy.toString()); pstmt = txn.prepareAutoCloseStatement(finalQuery.toString()); for (int i = 0; i < resourceIdList.size(); i++) { + pstmtLegacy.setLong(1 + i, resourceIdList.get(i)); pstmt.setLong(1 + i, resourceIdList.get(i)); } - ResultSet rs = pstmt.executeQuery(); + ResultSet rs = pstmtLegacy.executeQuery(); + while (rs.next()) { + result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); + } + rs = pstmt.executeQuery(); while (rs.next()) { result.put(rs.getString(1).concat(rs.getString(2)), rs.getLong(3)); } return result; } catch (SQLException e) { - throw new CloudRuntimeException("DB Exception on: " + finalQuery, e); + throw new CloudRuntimeException("DB Exception on: " + finalQueryLegacy, e); } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + finalQuery, e); + throw new CloudRuntimeException("Caught: " + finalQueryLegacy, e); } } @@ -1224,4 +1246,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return vms.stream() .collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId)); } + + @Override + public List listByIdsIncludingRemoved(List ids) { + SearchBuilder idsSearch = createSearchBuilder(); + idsSearch.and("ids", idsSearch.entity().getId(), SearchCriteria.Op.IN); + idsSearch.done(); + SearchCriteria sc = idsSearch.create(); + sc.setParameters("ids", ids.toArray()); + return listIncludingRemovedBy(sc); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java similarity index 84% rename from engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java rename to engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java index c22da6b4ff5..ea9ac5afba6 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDao.java @@ -19,7 +19,7 @@ package com.cloud.vm.dao; import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; import com.cloud.utils.db.GenericDao; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; -public interface UserVmDetailsDao extends GenericDao, ResourceDetailsDao { +public interface VMInstanceDetailsDao extends GenericDao, ResourceDetailsDao { } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java similarity index 81% rename from engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java rename to engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java index d8f751842d5..ca11b005fb2 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDetailsDaoImpl.java @@ -21,14 +21,14 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; -import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.VMInstanceDetailVO; @Component -public class UserVmDetailsDaoImpl extends ResourceDetailsDaoBase implements UserVmDetailsDao { +public class VMInstanceDetailsDaoImpl extends ResourceDetailsDaoBase implements VMInstanceDetailsDao { @Override public void addDetail(long resourceId, String key, String value, boolean display) { - super.addDetail(new UserVmDetailVO(resourceId, key, value, display)); + super.addDetail(new VMInstanceDetailVO(resourceId, key, value, display)); } } diff --git a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java index 0143aaa1e73..4045af58d4b 100644 --- a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDao.java @@ -27,6 +27,8 @@ public interface VMSnapshotDao extends GenericDao, StateDao< List findByVm(Long vmId); + List findByVmAndByType(Long vmId, VMSnapshot.Type type); + List listExpungingSnapshot(); List listByInstanceId(Long vmId, VMSnapshot.State... status); @@ -35,6 +37,8 @@ public interface VMSnapshotDao extends GenericDao, StateDao< List listByParent(Long vmSnapshotId); + List listByParentAndStateIn(Long vmSnapshotId, VMSnapshot.State... states); + VMSnapshotVO findByName(Long vmId, String name); List listByAccountId(Long accountId); diff --git a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java index 03a978f8546..83411b3cf8f 100644 --- a/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java @@ -42,6 +42,12 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem private final SearchBuilder SnapshotStatusSearch; private final SearchBuilder AllFieldsSearch; + private SearchBuilder parentIdEqAndStateIn; + + private static final String PARENT = "parent"; + + private static final String STATE = "state"; + protected VMSnapshotDaoImpl() { AllFieldsSearch = createSearchBuilder(); AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ); @@ -71,6 +77,11 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity().getVmId(), SearchCriteria.Op.EQ); SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity().getState(), SearchCriteria.Op.IN); SnapshotStatusSearch.done(); + + parentIdEqAndStateIn = createSearchBuilder(); + parentIdEqAndStateIn.and(PARENT, parentIdEqAndStateIn.entity().getParent(), Op.EQ); + parentIdEqAndStateIn.and(STATE, parentIdEqAndStateIn.entity().getState(), Op.IN); + parentIdEqAndStateIn.done(); } @Override @@ -80,6 +91,14 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem return listBy(sc, null); } + @Override + public List findByVmAndByType(Long vmId, VMSnapshot.Type type) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("vm_id", vmId); + sc.setParameters("vm_snapshot_type", type); + return listBy(sc, null); + } + @Override public List listExpungingSnapshot() { SearchCriteria sc = ExpungingSnapshotSearch.create(); @@ -111,6 +130,14 @@ public class VMSnapshotDaoImpl extends GenericDaoBase implem return listBy(sc, null); } + @Override + public List listByParentAndStateIn(Long vmSnapshotId, State... states) { + SearchCriteria sc = parentIdEqAndStateIn.create(); + sc.setParameters(PARENT, vmSnapshotId); + sc.setParameters(STATE, (Object[])states); + return listBy(sc, null); + } + @Override public VMSnapshotVO findByName(Long vmId, String name) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java new file mode 100644 index 00000000000..aaf63518708 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupDetailVO.java @@ -0,0 +1,98 @@ +// 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.backup; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "backup_details") +public class BackupDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "backup_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 65536) + private String value; + + @Column(name = "display") + private boolean display = true; + + public BackupDetailVO() { + } + + public BackupDetailVO(long backupId, String name, String value, boolean display) { + this.resourceId = backupId; + this.name = name; + this.value = value; + this.display = display; + } + + @Override + public long getId() { + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public boolean isDisplay() { + return display; + } + + public void setId(long id) { + this.id = id; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java index e8364520ed0..98efa94ceca 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java @@ -144,11 +144,21 @@ public class BackupRepositoryVO implements BackupRepository { return usedBytes; } + @Override + public void setUsedBytes(Long usedBytes) { + this.usedBytes = usedBytes; + } + @Override public Long getCapacityBytes() { return capacityBytes; } + @Override + public void setCapacityBytes(Long capacityBytes) { + this.capacityBytes = capacityBytes; + } + public Date getCreated() { return created; } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java index 0258c42c52b..37e8105e3d5 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupScheduleVO.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.backup; import java.util.Date; +import java.util.UUID; import javax.persistence.Column; import javax.persistence.Entity; @@ -39,6 +40,9 @@ public class BackupScheduleVO implements BackupSchedule { @Column(name = "id") private long id; + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + @Column(name = "vm_id") private Long vmId; @@ -59,18 +63,22 @@ public class BackupScheduleVO implements BackupSchedule { Long asyncJobId; @Column(name = "max_backups") - Integer maxBackups = 0; + private int maxBackups = 0; + + @Column(name = "quiescevm") + Boolean quiesceVM = false; public BackupScheduleVO() { } - public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, Integer maxBackups) { + public BackupScheduleVO(Long vmId, DateUtil.IntervalType scheduleType, String schedule, String timezone, Date scheduledTimestamp, int maxBackups, Boolean quiesceVM) { this.vmId = vmId; this.scheduleType = (short) scheduleType.ordinal(); this.schedule = schedule; this.timezone = timezone; this.scheduledTimestamp = scheduledTimestamp; this.maxBackups = maxBackups; + this.quiesceVM = quiesceVM; } @Override @@ -84,6 +92,11 @@ public class BackupScheduleVO implements BackupSchedule { return id; } + @Override + public String getUuid() { + return uuid; + } + public Long getVmId() { return vmId; } @@ -133,11 +146,19 @@ public class BackupScheduleVO implements BackupSchedule { this.asyncJobId = asyncJobId; } - public Integer getMaxBackups() { + public int getMaxBackups() { return maxBackups; } - public void setMaxBackups(Integer maxBackups) { + public void setMaxBackups(int maxBackups) { this.maxBackups = maxBackups; } + + public void setQuiesceVM(Boolean quiesceVM) { + this.quiesceVM = quiesceVM; + } + + public Boolean getQuiesceVM() { + return quiesceVM; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java index 9ef442baff9..0f8a10fb7be 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupVO.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.backup; import com.cloud.utils.db.GenericDao; import com.google.gson.Gson; + import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.lang3.StringUtils; @@ -26,6 +27,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.UUID; import javax.persistence.Column; @@ -38,6 +40,7 @@ import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.persistence.Transient; @Entity @Table(name = "backups") @@ -47,11 +50,17 @@ public class BackupVO implements Backup { @Column(name = "id") private long id; + @Column(name = "name") + private String name; + + @Column(name = "description") + private String description; + @Column(name = "uuid") private String uuid; @Column(name = "vm_id") - private long vmId; + private Long vmId; @Column(name = "external_id") private String externalId; @@ -88,12 +97,15 @@ public class BackupVO implements Backup { @Column(name = "zone_id") private long zoneId; - @Column(name = "backup_interval_type") - private short backupIntervalType; - @Column(name = "backed_volumes", length = 65535) protected String backedUpVolumes; + @Column(name = "backup_schedule_id") + private Long backupScheduleId; + + @Transient + Map details; + public BackupVO() { this.uuid = UUID.randomUUID().toString(); } @@ -115,11 +127,11 @@ public class BackupVO implements Backup { } @Override - public long getVmId() { + public Long getVmId() { return vmId; } - public void setVmId(long vmId) { + public void setVmId(Long vmId) { this.vmId = vmId; } @@ -211,14 +223,6 @@ public class BackupVO implements Backup { this.zoneId = zoneId; } - public short getBackupIntervalType() { - return backupIntervalType; - } - - public void setBackupIntervalType(short backupIntervalType) { - this.backupIntervalType = backupIntervalType; - } - @Override public Class getEntityType() { return Backup.class; @@ -226,7 +230,22 @@ public class BackupVO implements Backup { @Override public String getName() { - return null; + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public void setDescription(String description) { + this.description = description; } public List getBackedUpVolumes() { @@ -240,11 +259,33 @@ public class BackupVO implements Backup { this.backedUpVolumes = backedUpVolumes; } + @Override + public Map getDetails() { + return details; + } + + @Override + public String getDetail(String name) { + return this.details.get(name); + } + + public void setDetails(Map details) { + this.details = details; + } + public Date getRemoved() { return removed; } - public void setRemoved(Date removed) { this.removed = removed; } + + @Override + public Long getBackupScheduleId() { + return backupScheduleId; + } + + public void setBackupScheduleId(Long backupScheduleId) { + this.backupScheduleId = backupScheduleId; + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java index ffd5e5a4a66..e60e49e1a0c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDao.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.backup.dao; import java.util.List; -import org.apache.cloudstack.api.response.BackupResponse; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupVO; @@ -33,12 +32,14 @@ public interface BackupDao extends GenericDao { List listByVmId(Long zoneId, Long vmId); List listByAccountId(Long accountId); List syncBackups(Long zoneId, Long vmId, List externalBackups); + List listByVmIdAndOffering(Long zoneId, Long vmId, Long offeringId); + List searchByVmIds(List vmIds); BackupVO getBackupVO(Backup backup); List listByOfferingId(Long backupOfferingId); - - List listBackupsByVMandIntervalType(Long vmId, Backup.Type backupType); - - BackupResponse newBackupResponse(Backup backup); + List listVmIdsWithBackupsInZone(Long zoneId); public Long countBackupsForAccount(long accountId); public Long calculateBackupStorageForAccount(long accountId); + void loadDetails(BackupVO backup); + void saveDetails(BackupVO backup); + List listBySchedule(Long backupScheduleId); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java index b4e1a760282..fd29da72c71 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDaoImpl.java @@ -19,29 +19,31 @@ package org.apache.cloudstack.backup.dao; import java.util.ArrayList; import java.util.List; -import java.util.Objects; +import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericSearchBuilder; -import org.apache.cloudstack.api.response.BackupResponse; -import org.apache.cloudstack.backup.Backup; -import org.apache.cloudstack.backup.BackupOffering; -import org.apache.cloudstack.backup.BackupVO; -import com.cloud.dc.DataCenterVO; +import org.apache.cloudstack.backup.Backup; +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.backup.BackupVO; +import org.apache.commons.collections.CollectionUtils; + import com.cloud.dc.dao.DataCenterDao; -import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; -import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.vm.VMInstanceVO; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.vm.dao.VMInstanceDao; -import com.google.gson.Gson; +import com.cloud.network.dao.NetworkDao; public class BackupDaoImpl extends GenericDaoBase implements BackupDao { @@ -57,13 +59,26 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac @Inject VMInstanceDao vmInstanceDao; + @Inject + private VMTemplateDao templateDao; + @Inject BackupOfferingDao backupOfferingDao; + @Inject + BackupDetailsDao backupDetailsDao; + + @Inject + ServiceOfferingDao serviceOfferingDao; + + @Inject + NetworkDao networkDao; + private SearchBuilder backupSearch; private GenericSearchBuilder CountBackupsByAccount; private GenericSearchBuilder CalculateBackupStorageByAccount; - private SearchBuilder ListBackupsByVMandIntervalType; + private SearchBuilder listBackupsBySchedule; + private GenericSearchBuilder backupVmSearchInZone; public BackupDaoImpl() { } @@ -77,6 +92,11 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac backupSearch.and("zone_id", backupSearch.entity().getZoneId(), SearchCriteria.Op.EQ); backupSearch.done(); + backupVmSearchInZone = createSearchBuilder(Long.class); + backupVmSearchInZone.select(null, SearchCriteria.Func.DISTINCT, backupVmSearchInZone.entity().getVmId()); + backupVmSearchInZone.and("zone_id", backupVmSearchInZone.entity().getZoneId(), SearchCriteria.Op.EQ); + backupVmSearchInZone.done(); + CountBackupsByAccount = createSearchBuilder(Long.class); CountBackupsByAccount.select(null, SearchCriteria.Func.COUNT, null); CountBackupsByAccount.and("account", CountBackupsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); @@ -91,12 +111,11 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac CalculateBackupStorageByAccount.and("removed", CalculateBackupStorageByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); CalculateBackupStorageByAccount.done(); - ListBackupsByVMandIntervalType = createSearchBuilder(); - ListBackupsByVMandIntervalType.and("vmId", ListBackupsByVMandIntervalType.entity().getVmId(), SearchCriteria.Op.EQ); - ListBackupsByVMandIntervalType.and("intervalType", ListBackupsByVMandIntervalType.entity().getBackupIntervalType(), SearchCriteria.Op.EQ); - ListBackupsByVMandIntervalType.and("status", ListBackupsByVMandIntervalType.entity().getStatus(), SearchCriteria.Op.EQ); - ListBackupsByVMandIntervalType.and("removed", ListBackupsByVMandIntervalType.entity().getRemoved(), SearchCriteria.Op.NULL); - ListBackupsByVMandIntervalType.done(); + listBackupsBySchedule = createSearchBuilder(); + listBackupsBySchedule.and("backup_schedule_id", listBackupsBySchedule.entity().getBackupScheduleId(), SearchCriteria.Op.EQ); + listBackupsBySchedule.and("status", listBackupsBySchedule.entity().getStatus(), SearchCriteria.Op.EQ); + listBackupsBySchedule.and("removed", listBackupsBySchedule.entity().getRemoved(), SearchCriteria.Op.NULL); + listBackupsBySchedule.done(); } @Override @@ -130,6 +149,17 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac return new ArrayList<>(listBy(sc)); } + @Override + public List listByVmIdAndOffering(Long zoneId, Long vmId, Long offeringId) { + SearchCriteria sc = backupSearch.create(); + sc.setParameters("vm_id", vmId); + if (zoneId != null) { + sc.setParameters("zone_id", zoneId); + } + sc.setParameters("backup_offering_id", offeringId); + return new ArrayList<>(listBy(sc)); + } + private Backup findByExternalId(Long zoneId, String externalId) { SearchCriteria sc = backupSearch.create(); sc.setParameters("external_id", externalId); @@ -137,6 +167,18 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac return findOneBy(sc); } + @Override + public List searchByVmIds(List vmIds) { + if (CollectionUtils.isEmpty(vmIds)) { + return new ArrayList<>(); + } + SearchBuilder sb = createSearchBuilder(); + sb.and("vmIds", sb.entity().getVmId(), SearchCriteria.Op.IN); + SearchCriteria sc = sb.create(); + sc.setParameters("vmIds", vmIds.toArray()); + return search(sc, null); + } + public BackupVO getBackupVO(Backup backup) { BackupVO backupVO = new BackupVO(); backupVO.setExternalId(backup.getExternalId()); @@ -158,6 +200,27 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac expunge(sc); } + @Override + public BackupVO persist(BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + BackupVO backupDb = super.persist(backup); + saveDetails(backup); + loadDetails(backupDb); + return backupDb; + }); + } + + @Override + public boolean update(Long id, BackupVO backup) { + return Transaction.execute((TransactionCallback) status -> { + boolean result = super.update(id, backup); + if (result) { + saveDetails(backup); + } + return result; + }); + } + @Override public List syncBackups(Long zoneId, Long vmId, List externalBackups) { for (Backup backup : externalBackups) { @@ -171,7 +234,7 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac public Long countBackupsForAccount(long accountId) { SearchCriteria sc = CountBackupsByAccount.create(); sc.setParameters("account", accountId); - sc.setParameters("status", Backup.Status.Error, Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); + sc.setParameters("status", Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); return customSearch(sc, null).get(0); } @@ -179,58 +242,42 @@ public class BackupDaoImpl extends GenericDaoBase implements Bac public Long calculateBackupStorageForAccount(long accountId) { SearchCriteria sc = CalculateBackupStorageByAccount.create(); sc.setParameters("account", accountId); - sc.setParameters("status", Backup.Status.Error, Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); + sc.setParameters("status", Backup.Status.Failed, Backup.Status.Removed, Backup.Status.Expunged); return customSearch(sc, null).get(0).sum; } @Override - public List listBackupsByVMandIntervalType(Long vmId, Backup.Type backupType) { - SearchCriteria sc = ListBackupsByVMandIntervalType.create(); - sc.setParameters("vmId", vmId); - sc.setParameters("type", backupType.ordinal()); + public List listBySchedule(Long backupScheduleId) { + SearchCriteria sc = listBackupsBySchedule.create(); + sc.setParameters("backup_schedule_id", backupScheduleId); sc.setParameters("status", Backup.Status.BackedUp); - return listBy(sc, null); + return listBy(sc, new Filter(BackupVO.class, "date", true)); } @Override - public BackupResponse newBackupResponse(Backup backup) { - VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId()); - AccountVO account = accountDao.findByIdIncludingRemoved(vm.getAccountId()); - DomainVO domain = domainDao.findByIdIncludingRemoved(vm.getDomainId()); - DataCenterVO zone = dataCenterDao.findByIdIncludingRemoved(vm.getDataCenterId()); - Long offeringId = backup.getBackupOfferingId(); - if (offeringId == null) { - offeringId = vm.getBackupOfferingId(); - } - BackupOffering offering = backupOfferingDao.findByIdIncludingRemoved(offeringId); + public void loadDetails(BackupVO backup) { + Map details = backupDetailsDao.listDetailsKeyPairs(backup.getId()); + backup.setDetails(details); + } - BackupResponse response = new BackupResponse(); - response.setId(backup.getUuid()); - response.setVmId(vm.getUuid()); - response.setVmName(vm.getHostName()); - response.setExternalId(backup.getExternalId()); - response.setType(backup.getType()); - response.setDate(backup.getDate()); - response.setSize(backup.getSize()); - response.setProtectedSize(backup.getProtectedSize()); - response.setStatus(backup.getStatus()); - // ACS 4.20: For backups taken prior this release the backup.backed_volumes column would be empty hence use vm_instance.backup_volumes - String backedUpVolumes; - if (Objects.isNull(backup.getBackedUpVolumes())) { - backedUpVolumes = new Gson().toJson(vm.getBackupVolumeList().toArray(), Backup.VolumeInfo[].class); - } else { - backedUpVolumes = new Gson().toJson(backup.getBackedUpVolumes().toArray(), Backup.VolumeInfo[].class); + @Override + public void saveDetails(BackupVO backup) { + Map detailsStr = backup.getDetails(); + if (detailsStr == null) { + return; } - response.setVolumes(backedUpVolumes); - response.setBackupOfferingId(offering.getUuid()); - response.setBackupOffering(offering.getName()); - response.setAccountId(account.getUuid()); - response.setAccount(account.getAccountName()); - response.setDomainId(domain.getUuid()); - response.setDomain(domain.getName()); - response.setZoneId(zone.getUuid()); - response.setZone(zone.getName()); - response.setObjectName("backup"); - return response; + List details = new ArrayList(); + for (String key : detailsStr.keySet()) { + BackupDetailVO detail = new BackupDetailVO(backup.getId(), key, detailsStr.get(key), true); + details.add(detail); + } + backupDetailsDao.saveDetails(details); + } + + @Override + public List listVmIdsWithBackupsInZone(Long zoneId) { + SearchCriteria sc = backupVmSearchInZone.create(); + sc.setParameters("zone_id", zoneId); + return customSearchIncludingRemoved(sc, null); } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java new file mode 100644 index 00000000000..664650074bc --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDao.java @@ -0,0 +1,26 @@ +// 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.backup.dao; + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface BackupDetailsDao extends GenericDao, ResourceDetailsDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java new file mode 100644 index 00000000000..08c7192af90 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupDetailsDaoImpl.java @@ -0,0 +1,31 @@ +// 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.backup.dao; + + +import org.apache.cloudstack.backup.BackupDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.springframework.stereotype.Component; + +@Component +public class BackupDetailsDaoImpl extends ResourceDetailsDaoBase implements BackupDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value, boolean display) { + super.addDetail(new BackupDetailVO(resourceId, key, value, display)); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java index 0568a0185bb..9d67d07fe5e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupOfferingDaoImpl.java @@ -58,6 +58,7 @@ public class BackupOfferingDaoImpl extends GenericDaoBase listByZoneAndProvider(Long zoneId, String provider); BackupRepository findByBackupOfferingId(Long backupOfferingId); + + boolean updateCapacity(BackupRepository backupRepository, Long capacityBytes, Long usedBytes); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java index 460b6d8aba4..ea969988e2b 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java @@ -64,4 +64,12 @@ public class BackupRepositoryDaoImpl extends GenericDaoBase { + + List listByManagementServerId(long managementServerId); + + List listByHostId(long hostId); + + List listByState(Command.State... states); + + void removeCommand(long commandId, String commandName, Command.State state); + + ReconcileCommandVO findCommand(long reqSequence, String commandName); + + void updateCommandsToInterruptedByManagementServerId(long managementServerId); + + void updateCommandsToInterruptedByHostId(long hostId); + + List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java new file mode 100644 index 00000000000..593f464f9ad --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/command/dao/ReconcileCommandDaoImpl.java @@ -0,0 +1,134 @@ +// 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.command.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.api.ApiCommandResourceType; +import org.apache.cloudstack.command.ReconcileCommandVO; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.Command; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@DB +public class ReconcileCommandDaoImpl extends GenericDaoBase implements ReconcileCommandDao { + + final SearchBuilder updateCommandSearch; + final SearchBuilder resourceSearch; + + public ReconcileCommandDaoImpl() { + + updateCommandSearch = createSearchBuilder(); + updateCommandSearch.and("managementServerId", updateCommandSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByManagement", updateCommandSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + updateCommandSearch.and("hostId", updateCommandSearch.entity().getHostId(), SearchCriteria.Op.EQ); + updateCommandSearch.and("stateByAgent", updateCommandSearch.entity().getStateByAgent(), SearchCriteria.Op.IN); + updateCommandSearch.and("reqSequence", updateCommandSearch.entity().getRequestSequence(), SearchCriteria.Op.EQ); + updateCommandSearch.and("commandName", updateCommandSearch.entity().getCommandName(), SearchCriteria.Op.EQ); + updateCommandSearch.done(); + + resourceSearch = createSearchBuilder(); + resourceSearch.and("resourceId", resourceSearch.entity().getResourceId(), SearchCriteria.Op.EQ); + resourceSearch.and("resourceType", resourceSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + resourceSearch.and("stateByManagement", resourceSearch.entity().getStateByManagement(), SearchCriteria.Op.IN); + resourceSearch.done(); + } + + @Override + public List listByManagementServerId(long managementServerId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getManagementServerId(), SearchCriteria.Op.EQ, managementServerId); + return sc.list(); + } + + @Override + public List listByHostId(long hostId) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getHostId(), SearchCriteria.Op.EQ, hostId); + return sc.list(); + } + + @Override + public List listByState(Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } + + @Override + public void removeCommand(long reqSequence, String commandName, Command.State state) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + + ReconcileCommandVO vo = createForUpdate(); + if (state != null) { + vo.setStateByManagement(state); + } + vo.setRemoved(new Date()); + update(vo, sc); + } + + @Override + public ReconcileCommandVO findCommand(long reqSequence, String commandName) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("reqSequence", reqSequence); + sc.setParameters("commandName", commandName); + return findOneBy(sc); + } + + @Override + public void updateCommandsToInterruptedByManagementServerId(long managementServerId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("managementServerId", managementServerId); + sc.setParameters("stateByManagement", Command.State.CREATED, Command.State.RECONCILING); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByManagement(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public void updateCommandsToInterruptedByHostId(long hostId) { + SearchCriteria sc = updateCommandSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("stateByAgent", Command.State.STARTED, Command.State.PROCESSING, Command.State.PROCESSING_IN_BACKEND); + + ReconcileCommandVO vo = createForUpdate(); + vo.setStateByAgent(Command.State.INTERRUPTED); + + update(vo, sc); + } + + @Override + public List listByResourceIdAndTypeAndStates(long resourceId, ApiCommandResourceType resourceType, Command.State... states) { + QueryBuilder sc = QueryBuilder.create(ReconcileCommandVO.class); + sc.and(sc.entity().getResourceId(), SearchCriteria.Op.EQ, resourceId); + sc.and(sc.entity().getResourceType(), SearchCriteria.Op.EQ, resourceType); + sc.and(sc.entity().getStateByManagement(), SearchCriteria.Op.IN, (Object[]) states); + return sc.list(); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java new file mode 100644 index 00000000000..046a19c59fa --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeDetailsVO.java @@ -0,0 +1,91 @@ +// 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.gui.theme; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "gui_themes_details") +public class GuiThemeDetailsVO implements GuiThemeDetails { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "gui_theme_id", nullable = false) + private Long guiThemeId; + + @Column(name = "type", nullable = false, length = 100) + private String type; + + @Column(name = "value", nullable = false, length = 65535) + private String value; + + public GuiThemeDetailsVO() { + } + + public GuiThemeDetailsVO(Long guiThemeId, String type, String value) { + this.guiThemeId = guiThemeId; + this.type = type; + this.value = value; + } + + @Override + public long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public Long getGuiThemeId() { + return guiThemeId; + } + + @Override + public void setGuiThemeId(Long guiThemeId) { + this.guiThemeId = guiThemeId; + } + + @Override + public String getType() { + return type; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String value) { + this.value = value; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java new file mode 100644 index 00000000000..2df23b3d106 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeJoinVO.java @@ -0,0 +1,141 @@ +// 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.gui.theme; + +import com.cloud.utils.db.GenericDao; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; + +@Entity +@Table(name = "gui_themes_view") +public class GuiThemeJoinVO implements GuiThemeJoin { + @Id + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid; + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", nullable = false, length = 65535) + private String css; + + @Column(name = "json_configuration", nullable = false, length = 65535) + private String jsonConfiguration; + + @Column(name = "common_names", length = 65535) + private String commonNames; + + @Column(name = "domains", length = 65535) + private String domains; + + @Column(name = "accounts", length = 65535) + private String accounts; + + @Column(name = "recursive_domains") + private boolean recursiveDomains; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeJoinVO() { + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getCss() { + return css; + } + + @Override + public String getJsonConfiguration() { + return jsonConfiguration; + } + + @Override + public String getCommonNames() { + return commonNames; + } + + @Override + public String getDomains() { + return domains; + } + + @Override + public String getAccounts() { + return accounts; + } + + @Override + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + @Override + public boolean getIsPublic() { + return isPublic; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java new file mode 100644 index 00000000000..887e3886f6c --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/GuiThemeVO.java @@ -0,0 +1,189 @@ +// 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.gui.theme; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "gui_themes") +public class GuiThemeVO implements GuiTheme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", nullable = false) + private String uuid = UUID.randomUUID().toString(); + + @Column(name = "name", nullable = false, length = 2048) + private String name; + + @Column(name = "description", length = 4096) + private String description; + + @Column(name = "css", length = 65535) + private String css; + + @Column(name = "json_configuration", length = 65535) + private String jsonConfiguration; + + @Column(name = "is_public") + private boolean isPublic; + + @Column(name = "recursive_domains") + private boolean recursiveDomains = false; + + @Column(name = GenericDao.CREATED_COLUMN, nullable = false) + @Temporal(value = TemporalType.TIMESTAMP) + private Date created; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(value = TemporalType.TIMESTAMP) + private Date removed = null; + + public GuiThemeVO() { + + } + + public GuiThemeVO(String name, String description, String css, String jsonConfiguration, boolean recursiveDomains, boolean isPublic, Date created, Date removed) { + this.name = name; + this.description = description; + this.css = css; + this.jsonConfiguration = jsonConfiguration; + this.recursiveDomains = recursiveDomains; + this.isPublic = isPublic; + this.created = created; + this.removed = removed; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getCss() { + return css; + } + + @Override + public String getJsonConfiguration() { + return jsonConfiguration; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public boolean getIsPublic() { + return isPublic; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setCss(String css) { + this.css = css; + } + + @Override + public void setJsonConfiguration(String jsonConfiguration) { + this.jsonConfiguration = jsonConfiguration; + } + + @Override + public void setCreated(Date created) { + this.created = created; + } + + @Override + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public void setIsPublic(boolean isPublic) { + this.isPublic = isPublic; + } + + @Override + public boolean isRecursiveDomains() { + return recursiveDomains; + } + + @Override + public void setRecursiveDomains(boolean recursiveDomains) { + this.recursiveDomains = recursiveDomains; + } + + @Override + public String toString() { + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "description", "isPublic", "recursiveDomains"); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java new file mode 100644 index 00000000000..c00aedcc786 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDao.java @@ -0,0 +1,24 @@ +// 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 +// 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.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeVO; + +public interface GuiThemeDao extends GenericDao { + +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java new file mode 100644 index 00000000000..bc58c5f80f3 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDaoImpl.java @@ -0,0 +1,25 @@ +// 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.gui.theme.dao; + +import com.cloud.utils.db.GenericDaoBase; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +@Component +public class GuiThemeDaoImpl extends GenericDaoBase implements GuiThemeDao { +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java new file mode 100644 index 00000000000..af243b1ffa4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDao.java @@ -0,0 +1,30 @@ +// 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.gui.theme.dao; + +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; + +import java.util.List; + +public interface GuiThemeDetailsDao extends GenericDao { + List listGuiThemeIdsByCommonName(String commonName); + + List listGuiThemeIdsByDomainUuids(String domainUuid); + + void expungeByGuiThemeId(long guiThemeId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java new file mode 100644 index 00000000000..b0969833eb0 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeDetailsDaoImpl.java @@ -0,0 +1,126 @@ +// 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.gui.theme.dao; + +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.gui.theme.GuiThemeDetailsVO; +import org.apache.cloudstack.gui.theme.GuiThemeVO; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeDetailsDaoImpl extends GenericDaoBase implements GuiThemeDetailsDao { + + @Inject + DomainDao domainDao; + + @Inject + GuiThemeDao guiThemeDao; + + public List listGuiThemeIdsByCommonName(String commonName) { + GenericSearchBuilder detailsDaoSearchBuilder = createSearchBuilder(Long.class); + detailsDaoSearchBuilder.selectFields(detailsDaoSearchBuilder.entity().getGuiThemeId()); + detailsDaoSearchBuilder.and("commonNameType", detailsDaoSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + detailsDaoSearchBuilder.and().op("firstReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE); + detailsDaoSearchBuilder.or("secondReplace", detailsDaoSearchBuilder.entity().getValue(), SearchCriteria.Op.LIKE_REPLACE).cp(); + detailsDaoSearchBuilder.done(); + + SearchCriteria searchCriteria = detailsDaoSearchBuilder.create(); + searchCriteria.setParameters("commonNameType", "commonName"); + searchCriteria.setParameters("firstReplace", commonName, "*", "%"); + searchCriteria.setParameters("secondReplace", commonName, "*.", "%"); + + return customSearch(searchCriteria, null); + } + + public List listGuiThemeIdsByDomainUuids(String domainUuid) { + List guiThemeIds = new ArrayList<>(); + String requestedDomainPath = domainDao.findByUuid(domainUuid).getPath(); + + SearchBuilder domainSearchBuilderPathLike = domainDao.createSearchBuilder(); + domainSearchBuilderPathLike.and("pathLike", domainSearchBuilderPathLike.entity().getPath(), SearchCriteria.Op.LIKE_CONCAT); + + SearchBuilder domainSearchBuilderPathEq = domainDao.createSearchBuilder(); + domainSearchBuilderPathEq.and("pathEq", domainSearchBuilderPathEq.entity().getPath(), SearchCriteria.Op.EQ); + + GenericSearchBuilder detailsSearchBuilderPathLike = createDetailsSearchBuilder(domainSearchBuilderPathLike); + SearchCriteria searchCriteriaDomainPathLike = setParametersDomainPathLike(detailsSearchBuilderPathLike, requestedDomainPath); + + GenericSearchBuilder detailsSearchBuilderPathEq = createDetailsSearchBuilder(domainSearchBuilderPathEq); + SearchCriteria searchCriteriaDomainPathEq = setParametersDomainPathEq(detailsSearchBuilderPathEq, requestedDomainPath); + + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathLike, null)); + guiThemeIds.addAll(customSearch(searchCriteriaDomainPathEq, null)); + return guiThemeIds; + } + + private SearchCriteria setParametersDomainPathLike(GenericSearchBuilder detailsSearchBuilderPathLike, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathLike.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathLike", requestedDomainPath, "%"); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", true); + + return searchCriteria; + } + + private SearchCriteria setParametersDomainPathEq(GenericSearchBuilder detailsSearchBuilderPathEq, String requestedDomainPath) { + SearchCriteria searchCriteria = detailsSearchBuilderPathEq.create(); + searchCriteria.setParameters("domainUuidType", "domain"); + searchCriteria.setJoinParameters("domainJoin", "pathEq", requestedDomainPath); + searchCriteria.setJoinParameters("guiThemesJoin", "recursiveDomains", false); + + return searchCriteria; + } + + private GenericSearchBuilder createDetailsSearchBuilder(SearchBuilder domainSearchBuilder) { + SearchBuilder guiThemeDaoSearchBuilder = guiThemeDao.createSearchBuilder(); + guiThemeDaoSearchBuilder.and("recursiveDomains", guiThemeDaoSearchBuilder.entity().isRecursiveDomains(), SearchCriteria.Op.EQ); + + GenericSearchBuilder guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder = createSearchBuilder(Long.class); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.selectFields(guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId()); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.and("domainUuidType", guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getType(), SearchCriteria.Op.EQ); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("domainJoin", domainSearchBuilder, domainSearchBuilder.entity().getUuid(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getValue(), JoinBuilder.JoinType.INNER); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.join("guiThemesJoin", guiThemeDaoSearchBuilder, guiThemeDaoSearchBuilder.entity().getId(), + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.entity().getGuiThemeId(), JoinBuilder.JoinType.INNER); + + domainSearchBuilder.done(); + guiThemeDaoSearchBuilder.done(); + guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder.done(); + + return guiThemesDetailsJoinDomainJoinGuiThemesSearchBuilder; + } + + public void expungeByGuiThemeId(long guiThemeId) { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("guiThemeId", searchBuilder.entity().getGuiThemeId(), SearchCriteria.Op.EQ); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + searchCriteria.setParameters("guiThemeId", guiThemeId); + expunge(searchCriteria); + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java new file mode 100644 index 00000000000..740199cfca7 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDao.java @@ -0,0 +1,31 @@ +// 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.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.GenericDao; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; + +import java.util.List; + +public interface GuiThemeJoinDao extends GenericDao { + GuiThemeJoinVO findDefaultTheme(); + + Pair, Integer> listGuiThemesWithNoAuthentication(String commonName); + + Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, boolean showRemoved, Boolean showPublic); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java new file mode 100644 index 00000000000..ce6f7055812 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/gui/theme/dao/GuiThemeJoinDaoImpl.java @@ -0,0 +1,139 @@ +// 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.gui.theme.dao; + +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.gui.theme.GuiThemeJoinVO; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.List; + +@Component +public class GuiThemeJoinDaoImpl extends GenericDaoBase implements GuiThemeJoinDao { + @Inject + GuiThemeDetailsDao guiThemeDetailsDao; + + public static final Long INVALID_ID = -1L; + + public GuiThemeJoinVO findDefaultTheme() { + SearchBuilder searchBuilder = createSearchBuilder(); + searchBuilder.and("commonNames", searchBuilder.entity().getCommonNames(), SearchCriteria.Op.NULL); + searchBuilder.and("domainUuids", searchBuilder.entity().getDomains(), SearchCriteria.Op.NULL); + searchBuilder.and("accountUuids", searchBuilder.entity().getAccounts(), SearchCriteria.Op.NULL); + searchBuilder.done(); + + SearchCriteria searchCriteria = searchBuilder.create(); + + return findOneBy(searchCriteria); + } + + public Pair, Integer> listGuiThemesWithNoAuthentication(String commonName) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(null, null, commonName, null, null, null, false); + return searchOrderByCreatedDate(searchCriteria, false); + } + + public Pair, Integer> listGuiThemes(Long id, String name, String commonName, String domainUuid, String accountUuid, boolean listAll, + boolean showRemoved, Boolean showPublic) { + SearchCriteria searchCriteria = createGuiThemeSearchCriteria(id, name, commonName, domainUuid, accountUuid, showPublic, listAll); + + if (listAll) { + showRemoved = false; + } + + return searchOrderByCreatedDate(searchCriteria, showRemoved); + } + + private Pair, Integer> searchOrderByCreatedDate(SearchCriteria searchCriteria, boolean showRemoved) { + Filter filter = new Filter(GuiThemeJoinVO.class, "created", false); + return searchAndCount(searchCriteria, filter, showRemoved); + } + + private SearchCriteria createGuiThemeSearchCriteria(Long id, String name, String commonName, String domainUuid, String accountUuid, Boolean showPublic, boolean listAll) { + SearchCriteria searchCriteria = createGuiThemeJoinSearchBuilder(listAll, showPublic).create(); + List idList = new ArrayList<>(); + + if (id != null) { + idList.add(id); + } + + searchCriteria.setParametersIfNotNull("name", name); + searchCriteria.setParametersIfNotNull("isPublic", showPublic); + + if (StringUtils.isNotBlank(accountUuid)) { + searchCriteria.setParameters("accountUuid", "%" + accountUuid + "%"); + } + + if (StringUtils.isNotBlank(commonName)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.COMMON_NAME, commonName); + } + + if (StringUtils.isNotBlank(domainUuid)) { + setGuiThemeIdsFilteredByType(idList, ApiConstants.DOMAIN, domainUuid); + } + + searchCriteria.setParametersIfNotNull("idIn", idList.toArray()); + + return searchCriteria; + } + + /** + * Sets the `id IN ( )` clause of the query. If the informed value of common name or domain ID does not retrieve any GUI theme ID; then, an invalid ID (-1) is passed to the + * list, as not a single entity has this ID. This is necessary as to set the parameter even if it did not find any GUI theme ID; otherwise, the query would not filter the + * common name or domain ID passed. + */ + public void setGuiThemeIdsFilteredByType(List idList, String type, String value) { + List guiThemeIdsFilteredByType = new ArrayList<>(); + + switch (type) { + case ApiConstants.COMMON_NAME: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByCommonName(value); + break; + case ApiConstants.DOMAIN: + guiThemeIdsFilteredByType = guiThemeDetailsDao.listGuiThemeIdsByDomainUuids(value); + break; + } + + if (CollectionUtils.isNotEmpty(guiThemeIdsFilteredByType)) { + idList.addAll(guiThemeIdsFilteredByType); + return; + } + logger.trace(String.format("No GUI theme with the specified [%s] with UUID [%s] was found, adding an invalid ID for filtering.", type, value)); + idList.add(INVALID_ID); + } + + private SearchBuilder createGuiThemeJoinSearchBuilder(boolean listAll, Boolean showPublic) { + SearchBuilder guiThemeJoinSearchBuilder = createSearchBuilder(); + guiThemeJoinSearchBuilder.and("idIn", guiThemeJoinSearchBuilder.entity().getId(), SearchCriteria.Op.IN); + guiThemeJoinSearchBuilder.and("name", guiThemeJoinSearchBuilder.entity().getName(), SearchCriteria.Op.EQ); + guiThemeJoinSearchBuilder.and("accountUuid", guiThemeJoinSearchBuilder.entity().getAccounts(), SearchCriteria.Op.LIKE); + + if (!listAll && showPublic != null) { + guiThemeJoinSearchBuilder.and("isPublic", guiThemeJoinSearchBuilder.entity().getIsPublic(), SearchCriteria.Op.EQ); + } + + return guiThemeJoinSearchBuilder; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java index 6b6fe200c10..1102de16e4e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.cloudstack.api.ResourceDetail; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericDao; public interface ResourceDetailsDao extends GenericDao { @@ -32,6 +33,13 @@ public interface ResourceDetailsDao extends GenericDao */ R findDetail(long resourceId, String name); + /** + * Find details by key + * @param key + * @return + */ + List findDetails(String key); + /** * Find details by resourceId and key * @param resourceId @@ -94,6 +102,8 @@ public interface ResourceDetailsDao extends GenericDao Map listDetailsVisibility(long resourceId); + Pair, Map> listDetailsKeyPairsWithVisibility(long resourceId); + void saveDetails(List details); void addDetail(long resourceId, String key, String value, boolean display); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java index 29d3f88fd90..eafaed182ab 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; +import com.cloud.utils.Pair; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; @@ -64,6 +65,12 @@ public abstract class ResourceDetailsDaoBase extends G return findOneBy(sc); } + public List findDetails(String key) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("name", key); + return listBy(sc); + } + public List findDetails(long resourceId, String key) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); @@ -127,6 +134,19 @@ public abstract class ResourceDetailsDaoBase extends G return details; } + @Override + public Pair, Map> listDetailsKeyPairsWithVisibility(long resourceId) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("resourceId", resourceId); + List results = search(sc, null); + Map> partitioned = results.stream() + .collect(Collectors.partitioningBy( + R::isDisplay, + Collectors.toMap(R::getName, R::getValue) + )); + return new Pair<>(partitioned.get(true), partitioned.get(false)); + } + public List listDetails(long resourceId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("resourceId", resourceId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java index 7aab5bbf7b3..eda4bcfdaa1 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java @@ -44,7 +44,7 @@ public interface ImageStoreDao extends GenericDao { List listStoresByZoneId(long zoneId); - List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role); + List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long storeId); List findByProtocol(String protocol); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java index 84b88c215ca..4cb40b5eaf6 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ImageStoreDaoImpl.java @@ -41,7 +41,7 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem private SearchBuilder nameSearch; private SearchBuilder providerSearch; private SearchBuilder regionSearch; - private SearchBuilder storeSearch; + private SearchBuilder storesExceptIdSearch; private SearchBuilder protocolSearch; private SearchBuilder zoneProtocolSearch; @@ -88,11 +88,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem regionSearch.and("role", regionSearch.entity().getRole(), SearchCriteria.Op.EQ); regionSearch.done(); - storeSearch = createSearchBuilder(); - storeSearch.and("providerName", storeSearch.entity().getProviderName(), SearchCriteria.Op.EQ); - storeSearch.and("role", storeSearch.entity().getRole(), SearchCriteria.Op.EQ); - storeSearch.and("dataCenterId", storeSearch.entity().getDcId(), SearchCriteria.Op.EQ); - storeSearch.done(); + storesExceptIdSearch = createSearchBuilder(); + storesExceptIdSearch.and("providerName", storesExceptIdSearch.entity().getProviderName(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("role", storesExceptIdSearch.entity().getRole(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("dataCenterId", storesExceptIdSearch.entity().getDcId(), SearchCriteria.Op.EQ); + storesExceptIdSearch.and("id", storesExceptIdSearch.entity().getId(), SearchCriteria.Op.NEQ); + storesExceptIdSearch.done(); return true; } @@ -113,11 +114,12 @@ public class ImageStoreDaoImpl extends GenericDaoBase implem } @Override - public List listAllStoresInZone(Long zoneId, String provider, DataStoreRole role) { - SearchCriteria sc = storeSearch.create(); + public List listAllStoresInZoneExceptId(Long zoneId, String provider, DataStoreRole role, long id) { + SearchCriteria sc = storesExceptIdSearch.create(); sc.setParameters("providerName", provider); sc.setParameters("role", role); sc.setParameters("dataCenterId", zoneId); + sc.setParameters("id", id); return listBy(sc); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java index 94f6b5ec372..695742823eb 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDao.java @@ -39,4 +39,6 @@ public interface ObjectStoreDao extends GenericDao { ObjectStoreResponse setObjectStoreResponse(ObjectStoreResponse storeData, ObjectStoreVO store); Integer countAllObjectStores(); + + Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java index 51abde013b6..891ac0996ac 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreDaoImpl.java @@ -21,6 +21,10 @@ package org.apache.cloudstack.storage.datastore.db; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionStatus; + import org.apache.cloudstack.api.response.ObjectStoreResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.springframework.stereotype.Component; @@ -142,6 +146,19 @@ public class ObjectStoreDaoImpl extends GenericDaoBase impl ObjectStoreResponse osResponse = new ObjectStoreResponse(); osResponse.setId(store.getUuid()); osResponse.setName(store.getName()); + if (store.getTotalSize() != null && store.getTotalSize() != 0L) { + osResponse.setStorageTotal(store.getTotalSize()); + } + if (store.getUsedSize() == null) { + osResponse.setStorageUsed(0L); + } else { + osResponse.setStorageUsed(store.getUsedSize()); + } + if (store.getAllocatedSize() == null) { + osResponse.setStorageAllocated(0L); + } else { + osResponse.setStorageAllocated(store.getAllocatedSize()); + } osResponse.setProviderName(store.getProviderName()); String url = store.getUrl(); osResponse.setUrl(url); @@ -159,4 +176,19 @@ public class ObjectStoreDaoImpl extends GenericDaoBase impl SearchCriteria sc = createSearchCriteria(); return getCount(sc); } + + @Override + public Boolean updateAllocatedSize(ObjectStoreVO objectStoreVO, long delta) { + return Transaction.execute(new TransactionCallback() { + @Override + public Boolean doInTransaction(final TransactionStatus status) { + if (objectStoreVO.getAllocatedSize() != null) { + objectStoreVO.setAllocatedSize(objectStoreVO.getAllocatedSize() + delta); + } else { + objectStoreVO.setAllocatedSize(delta); + } + return update(objectStoreVO.getId(), objectStoreVO); + } + }); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java index 18cc06a6573..23b650acc79 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/ObjectStoreVO.java @@ -60,8 +60,11 @@ public class ObjectStoreVO implements ObjectStore { @Column(name = "total_size") private Long totalSize; - @Column(name = "used_bytes") - private Long usedBytes; + @Column(name = "used_size") + private Long usedSize; + + @Column(name = "allocated_size") + private Long allocatedSize; @Transient Map details; @@ -130,18 +133,26 @@ public class ObjectStoreVO implements ObjectStore { this.totalSize = totalSize; } - public Long getUsedBytes() { - return usedBytes; + public Long getUsedSize() { + return usedSize; } - public void setUsedBytes(Long usedBytes) { - this.usedBytes = usedBytes; + public void setUsedSize(Long usedSize) { + this.usedSize = usedSize; } public void setDetails(Map details) { this.details = details; } + public Long getAllocatedSize() { + return allocatedSize; + } + + public void setAllocatedSize(Long allocatedSize) { + this.allocatedSize = allocatedSize; + } + @Override public String toString() { return String.format("ObjectStore %s", diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index f0c235e842c..37aa70abb6e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -58,7 +58,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ void updateCapacityIops(long id, long capacityIops); - StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule); + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups); + + StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups); /** * Find pool by name. @@ -82,7 +84,9 @@ public interface PrimaryDataStoreDao extends GenericDao { */ List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope); - List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout); + + List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups); List findDisabledPoolsByScope(long dcId, Long podId, Long clusterId, ScopeType scope); @@ -103,6 +107,8 @@ public interface PrimaryDataStoreDao extends GenericDao { void updateDetails(long poolId, Map details); + void removeDetails(long poolId); + Map getDetails(long poolId); List searchForStoragePoolTags(long poolId); @@ -123,6 +129,10 @@ public interface PrimaryDataStoreDao extends GenericDao { List findZoneWideStoragePoolsByTags(long dcId, String[] tags, boolean validateTagRule); + List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups); + + List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type); + List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType); List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword); @@ -139,6 +149,8 @@ public interface PrimaryDataStoreDao extends GenericDao { void deletePoolTags(long poolId); + void deleteStoragePoolAccessGroups(long poolId); + List listChildStoragePoolsInDatastoreCluster(long poolId); Integer countAll(); @@ -150,8 +162,13 @@ public interface PrimaryDataStoreDao extends GenericDao { List listStoragePoolsWithActiveVolumesByOfferingId(long offeringid); Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId, - String path, Long podId, Long clusterId, String address, ScopeType scopeType, StoragePoolStatus status, - String keyword, Filter searchFilter); + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter); List listByIds(List ids); + + List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType); + + List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId); + } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index cb7313954dc..8b230d03154 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -28,6 +28,8 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.storage.StoragePoolAndAccessGroupMapVO; +import com.cloud.storage.dao.StoragePoolAndAccessGroupMapDao; import org.apache.commons.collections.CollectionUtils; import com.cloud.host.Status; @@ -70,15 +72,25 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase private StoragePoolHostDao _hostDao; @Inject private StoragePoolTagsDao _tagsDao; + @Inject + StoragePoolAndAccessGroupMapDao _storagePoolAccessGroupMapDao; protected final String DetailsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_details ON storage_pool.id = storage_pool_details.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String DetailsSqlSuffix = ") GROUP BY storage_pool_details.pool_id HAVING COUNT(storage_pool_details.name) >= ?"; + protected final String DetailsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_details.pool_id"; private final String ZoneWideTagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; private final String ZoneWideTagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.hypervisor = ? and storage_pool.data_center_id = ? and storage_pool.scope = ? and ("; + private final String ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; // Storage tags are now separate from storage_pool_details, leaving only details on that table protected final String TagsSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_tags ON storage_pool.id = storage_pool_tags.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' AND storage_pool_tags.is_tag_a_rule = 0 and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; protected final String TagsSqlSuffix = ") GROUP BY storage_pool_tags.pool_id HAVING COUNT(storage_pool_tags.tag) >= ?"; + protected final String SAGsForHostConnectionSqlPrefix = "SELECT storage_pool.* from storage_pool LEFT JOIN storage_pool_and_access_group_map ON storage_pool.id = storage_pool_and_access_group_map.pool_id WHERE storage_pool.removed is null and storage_pool.status = 'Up' and storage_pool.data_center_id = ? and (storage_pool.pod_id = ? or storage_pool.pod_id is null) and storage_pool.scope = ? and ("; + + protected final String SAGsForHostConnectionSqlSuffix = ") GROUP BY storage_pool_and_access_group_map.pool_id"; private static final String GET_STORAGE_POOLS_OF_VOLUMES_WITHOUT_OR_NOT_HAVING_TAGS = "SELECT s.* " + "FROM volumes vol " + @@ -295,21 +307,29 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return findOneIncludingRemovedBy(sc); } + @Override + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, List storageAccessGroups) { + return persist(pool, details, tags, isTagARule, true, storageAccessGroups); + } + @Override @DB - public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule) { + public StoragePoolVO persist(StoragePoolVO pool, Map details, List tags, Boolean isTagARule, boolean displayDetails, List storageAccessGroups) { TransactionLegacy txn = TransactionLegacy.currentTxn(); txn.start(); pool = super.persist(pool); if (details != null) { for (Map.Entry detail : details.entrySet()) { - StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), true); + StoragePoolDetailVO vo = new StoragePoolDetailVO(pool.getId(), detail.getKey(), detail.getValue(), displayDetails); _detailsDao.persist(vo); } } if (CollectionUtils.isNotEmpty(tags)) { _tagsDao.persist(pool.getId(), tags, isTagARule); } + if (CollectionUtils.isNotEmpty(storageAccessGroups)) { + _storagePoolAccessGroupMapDao.persist(pool.getId(), storageAccessGroups); + } txn.commit(); return pool; } @@ -333,6 +353,13 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, valuesLength); } + protected List findPoolsByDetailsOrTagsForHostConnectionInternal(long dcId, long podId, Long clusterId, ScopeType scope, String sqlValues, ValueType valuesType) { + String sqlPrefix = valuesType.equals(ValueType.DETAILS) ? DetailsSqlPrefix : SAGsForHostConnectionSqlPrefix; + String sqlSuffix = valuesType.equals(ValueType.DETAILS) ? DetailsForHostConnectionSqlSuffix : SAGsForHostConnectionSqlSuffix; + String sql = getSqlPreparedStatement(sqlPrefix, sqlSuffix, sqlValues, clusterId); + return searchStoragePoolsPreparedStatement(sql, dcId, podId, clusterId, scope, null); + } + /** * Search storage pools in a transaction * @param sql prepared statement sql @@ -344,7 +371,50 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase * @return storage pools matching criteria */ @DB - protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, int valuesLength) { + protected List searchStoragePoolsWithHypervisorTypesPreparedStatement(String sql, HypervisorType type, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + List pools = new ArrayList(); + try (PreparedStatement pstmt = txn.prepareStatement(sql);) { + if (pstmt != null) { + int i = 1; + pstmt.setString(i++, type.toString()); + pstmt.setLong(i++, dcId); + if (podId != null) { + pstmt.setLong(i++, podId); + } + pstmt.setString(i++, scope.toString()); + if (clusterId != null) { + pstmt.setLong(i++, clusterId); + } + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } + try (ResultSet rs = pstmt.executeQuery();) { + while (rs.next()) { + pools.add(toEntityBean(rs, false)); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to execute :" + e.getMessage(), e); + } + return pools; + } + + /** + * Search storage pools in a transaction + * @param sql prepared statement sql + * @param dcId data center id + * @param podId pod id + * @param clusterId cluster id + * @param scope scope + * @param valuesLength values length + * @return storage pools matching criteria + */ + @DB + protected List searchStoragePoolsPreparedStatement(String sql, long dcId, Long podId, Long clusterId, ScopeType scope, Integer valuesLength) { TransactionLegacy txn = TransactionLegacy.currentTxn(); List pools = new ArrayList(); try (PreparedStatement pstmt = txn.prepareStatement(sql);) { @@ -358,7 +428,9 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase if (clusterId != null) { pstmt.setLong(i++, clusterId); } - pstmt.setInt(i++, valuesLength); + if (valuesLength != null) { + pstmt.setInt(i++, valuesLength); + } try (ResultSet rs = pstmt.executeQuery();) { while (rs.next()) { pools.add(toEntityBean(rs, false)); @@ -415,6 +487,22 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return sqlValues.toString(); } + /** + * Return SQL string from storage pool access group map, to be placed between SQL Prefix and SQL Suffix when creating storage tags PreparedStatement. + * @param storageAccessGroups storage tags array + * @return SQL string containing storage tag values to be placed between Prefix and Suffix when creating PreparedStatement. + * @throws NullPointerException if tags is null + * @throws IndexOutOfBoundsException if tags is not null, but empty + */ + protected String getSqlValuesFromStorageAccessGroups(String[] storageAccessGroups) throws NullPointerException, IndexOutOfBoundsException { + StringBuilder sqlValues = new StringBuilder(); + for (String tag : storageAccessGroups) { + sqlValues.append("(storage_pool_and_access_group_map.storage_access_group='").append(tag).append("') OR "); + } + sqlValues.delete(sqlValues.length() - 4, sqlValues.length()); + return sqlValues.toString(); + } + @DB @Override public List findPoolsByDetails(long dcId, long podId, Long clusterId, Map details, ScopeType scope) { @@ -423,10 +511,10 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } @Override - public List findPoolsByTags(long dcId, long podId, Long clusterId, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { + public List findPoolsByTags(long dcId, long podId, Long clusterId, ScopeType scope, String[] tags, boolean validateTagRule, long ruleExecuteTimeout) { List storagePools = null; if (tags == null || tags.length == 0) { - storagePools = listBy(dcId, podId, clusterId, ScopeType.CLUSTER); + storagePools = listBy(dcId, podId, clusterId, scope); if (validateTagRule) { storagePools = getPoolsWithoutTagRule(storagePools); @@ -434,7 +522,20 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } else { String sqlValues = getSqlValuesFromStorageTags(tags); - storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, ScopeType.CLUSTER, sqlValues, ValueType.TAGS, tags.length); + storagePools = findPoolsByDetailsOrTagsInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS, tags.length); + } + + return storagePools; + } + + @Override + public List findPoolsByAccessGroupsForHostConnection(Long dcId, Long podId, Long clusterId, ScopeType scope, String[] storageAccessGroups) { + List storagePools = null; + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + storagePools = listBy(dcId, podId, clusterId, scope); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + storagePools = findPoolsByDetailsOrTagsForHostConnectionInternal(dcId, podId, clusterId, scope, sqlValues, ValueType.TAGS); } return storagePools; @@ -551,6 +652,77 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return storagePoolsToReturn; } + @Override + public List findZoneWideStoragePoolsByAccessGroupsForHostConnection(long dcId, String[] storageAccessGroups) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsForHostConnectionSqlPrefix, ZoneWideStorageAccessGroupsForHostConnectionSqlSuffix, sqlValues, null); + return searchStoragePoolsPreparedStatement(sql, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findZoneWideStoragePoolsByAccessGroupsAndHypervisorTypeForHostConnection(long dcId, String[] storageAccessGroups, HypervisorType type) { + if (storageAccessGroups == null || storageAccessGroups.length == 0) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dcId); + sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + sc.and(sc.entity().getHypervisor(), Op.EQ, type); + return sc.list(); + } else { + String sqlValues = getSqlValuesFromStorageAccessGroups(storageAccessGroups); + String sql = getSqlPreparedStatement(ZoneWideStorageAccessGroupsWithHypervisorTypeSqlPrefix, ZoneWideStorageAccessGroupsWithHypervisorTypeSqlSuffix, sqlValues, null); + return searchStoragePoolsWithHypervisorTypesPreparedStatement(sql, type, dcId, null, null, ScopeType.ZONE, null); + } + } + + @Override + public List findStoragePoolsByEmptyStorageAccessGroups(Long dcId, Long podId, Long clusterId, ScopeType scope, HypervisorType hypervisorType) { + SearchBuilder poolSearch = createSearchBuilder(); + SearchBuilder storageAccessGroupsPoolSearch = _storagePoolAccessGroupMapDao.createSearchBuilder(); + // Set criteria for pools + poolSearch.and("scope", poolSearch.entity().getScope(), Op.EQ); + poolSearch.and("removed", poolSearch.entity().getRemoved(), Op.NULL); + poolSearch.and("status", poolSearch.entity().getStatus(), Op.EQ); + poolSearch.and("datacenterid", poolSearch.entity().getDataCenterId(), Op.EQ); + poolSearch.and("podid", poolSearch.entity().getPodId(), Op.EQ); + poolSearch.and("clusterid", poolSearch.entity().getClusterId(), Op.EQ); + poolSearch.and("hypervisortype", poolSearch.entity().getHypervisor(), Op.EQ); + + // Set StoragePoolAccessGroupMapVO.pool_id IS NULL. This ensures only pools without tags are returned + storageAccessGroupsPoolSearch.and("poolid", storageAccessGroupsPoolSearch.entity().getPoolId(), Op.NULL); + poolSearch.join("tagJoin", storageAccessGroupsPoolSearch, poolSearch.entity().getId(), storageAccessGroupsPoolSearch.entity().getPoolId(), JoinBuilder.JoinType.LEFT); + + SearchCriteria sc = poolSearch.create(); + sc.setParameters("scope", scope.toString()); + sc.setParameters("status", Status.Up.toString()); + + if (dcId != null) { + sc.setParameters("datacenterid", dcId); + } + + if (podId != null) { + sc.setParameters("podid", podId); + } + + if (clusterId != null) { + sc.setParameters("clusterid", clusterId); + } + + if (hypervisorType != null) { + sc.setParameters("hypervisortype", hypervisorType); + } + + return listBy(sc); + } + @Override public List searchForStoragePoolTags(long poolId) { return _tagsDao.getStoragePoolTags(poolId); @@ -570,6 +742,11 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase } } + @Override + public void removeDetails(long poolId) { + _detailsDao.removeDetails(poolId); + } + @Override public Map getDetails(long poolId) { return _detailsDao.listDetailsKeyPairs(poolId); @@ -649,6 +826,11 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase _tagsDao.deleteTags(poolId); } + @Override + public void deleteStoragePoolAccessGroups(long poolId) { + _storagePoolAccessGroupMapDao.deleteStorageAccessGroups(poolId); + } + @Override public List listChildStoragePoolsInDatastoreCluster(long poolId) { QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); @@ -715,9 +897,10 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase @Override public Pair, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId, - String path, Long podId, Long clusterId, String address, ScopeType scopeType, StoragePoolStatus status, - String keyword, Filter searchFilter) { - SearchCriteria sc = createStoragePoolSearchCriteria(storagePoolId, storagePoolName, zoneId, path, podId, clusterId, address, scopeType, status, keyword); + String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, StoragePoolStatus status, + String keyword, String storageAccessGroup, Filter searchFilter) { + SearchCriteria sc = createStoragePoolSearchCriteria(storagePoolId, storagePoolName, zoneId, path, podId, clusterId, + hostId, address, scopeType, status, keyword, storageAccessGroup); Pair, Integer> uniquePair = searchAndCount(sc, searchFilter); List idList = uniquePair.first().stream().map(StoragePoolVO::getId).collect(Collectors.toList()); return new Pair<>(idList, uniquePair.second()); @@ -733,9 +916,17 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase return listBy(sc); } + @Override + public List findPoolsByStorageTypeAndZone(Storage.StoragePoolType storageType, Long zoneId) { + SearchCriteria sc = AllFieldSearch.create(); + sc.setParameters("poolType", storageType); + sc.addAnd("dataCenterId", Op.EQ, zoneId); + return listBy(sc); + } + private SearchCriteria createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName, - Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType, - StoragePoolStatus status, String keyword) { + Long zoneId, String path, Long podId, Long clusterId, Long hostId, String address, ScopeType scopeType, + StoragePoolStatus status, String keyword, String storageAccessGroup) { SearchBuilder sb = createSearchBuilder(); sb.select(null, SearchCriteria.Func.DISTINCT, sb.entity().getId()); // select distinct // ids @@ -750,6 +941,18 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase sb.and("status", sb.entity().getStatus(), SearchCriteria.Op.EQ); sb.and("parent", sb.entity().getParent(), SearchCriteria.Op.EQ); + if (hostId != null) { + SearchBuilder hostJoin = _hostDao.createSearchBuilder(); + hostJoin.and("hostId", hostJoin.entity().getHostId(), SearchCriteria.Op.EQ); + sb.join("poolHostJoin", hostJoin, sb.entity().getId(), hostJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + + if (storageAccessGroup != null) { + SearchBuilder storageAccessGroupJoin = _storagePoolAccessGroupMapDao.createSearchBuilder(); + storageAccessGroupJoin.and("storageAccessGroup", storageAccessGroupJoin.entity().getStorageAccessGroup(), SearchCriteria.Op.EQ); + sb.join("poolStorageAccessGroupJoin", storageAccessGroupJoin, sb.entity().getId(), storageAccessGroupJoin.entity().getPoolId(), JoinBuilder.JoinType.INNER); + } + SearchCriteria sc = sb.create(); if (keyword != null) { @@ -798,6 +1001,15 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase sc.setParameters("status", status.toString()); } sc.setParameters("parent", 0); + + if (hostId != null) { + sc.setJoinParameters("poolHostJoin", "hostId", hostId); + } + + if (storageAccessGroup != null) { + sc.setJoinParameters("poolStorageAccessGroupJoin", "storageAccessGroup", storageAccessGroup); + } + return sc; } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index 4cd29b465ee..902cb73dc05 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.storage.datastore.db; import java.util.Date; import java.util.List; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -46,12 +47,31 @@ StateDao listBySnapshot(long snapshotId, DataStoreRole role); + SnapshotDataStoreVO findParent(DataStoreRole role, Long storeId, Long zoneId, Long volumeId, boolean kvmIncrementalSnapshot, Hypervisor.HypervisorType hypervisorType); + + SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, ObjectInDataStoreStateMachine.State state); + + List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId); + + SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId); + + List listBySnapshotId(long snapshotId); + + List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role); + + List listExtractedSnapshotsBeforeDate(Date beforeDate); + + List listSnapshotsBySnapshotId(long snapshotId); List listReadyBySnapshot(long snapshotId, DataStoreRole role); + List listReadyBySnapshotId(long snapshotId); SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role); + List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId); + + SnapshotDataStoreVO findBySnapshotIdInAnyState(long snapshotId, DataStoreRole role); + List listDestroyed(long storeId); List findBySnapshotId(long snapshotId); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java index 5bf67eb3881..241c3df2e4a 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDaoImpl.java @@ -16,24 +16,6 @@ // under the License. package org.apache.cloudstack.storage.datastore.db; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; -import org.apache.commons.collections.CollectionUtils; -import org.springframework.stereotype.Component; - import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.SnapshotVO; @@ -47,6 +29,25 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.UpdateBuilder; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; + +import org.apache.commons.collections.CollectionUtils; + +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + @Component public class SnapshotDataStoreDaoImpl extends GenericDaoBase implements SnapshotDataStoreDao { private static final String STORE_ID = "store_id"; @@ -58,26 +59,43 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase searchFilteringStoreIdEqStoreRoleEqStateNeqRefCntNeq; protected SearchBuilder searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq; private SearchBuilder stateSearch; - private SearchBuilder idStateNeqSearch; + private SearchBuilder idStateNinSearch; protected SearchBuilder snapshotVOSearch; private SearchBuilder snapshotCreatedSearch; private SearchBuilder dataStoreAndInstallPathSearch; private SearchBuilder storeAndSnapshotIdsSearch; private SearchBuilder storeSnapshotDownloadStatusSearch; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull; + private SearchBuilder searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore; + private SearchBuilder searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq; + + private SearchBuilder searchBySnapshotId; protected static final List HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING = List.of(Hypervisor.HypervisorType.XenServer); @Inject protected SnapshotDao snapshotDao; + @Inject + protected ImageStoreDao imageStoreDao; + private static final String FIND_OLDEST_OR_LATEST_SNAPSHOT = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + " store_role = ? and volume_id = ? and state = 'Ready'" + " order by created %s " + " limit 1"; + private static final String FIND_SNAPSHOT_IN_ZONE = "SELECT ssr.* FROM " + + "snapshot_store_ref ssr, snapshots s " + + "WHERE ssr.snapshot_id=? AND ssr.snapshot_id = s.id AND s.data_center_id=?;"; + @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); @@ -119,10 +137,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + SearchCriteria sc; + if (kvmIncrementalSnapshot && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + } else { + sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEq.create(); + } + sc.setParameters(VOLUME_ID, volumeId); - sc.setParameters(STORE_ROLE, role.toString()); + if (role != null) { + sc.setParameters(STORE_ROLE, role.toString()); + } sc.setParameters(STATE, ObjectInDataStoreStateMachine.State.Ready.name()); - sc.setParameters(STORE_ID, storeId); + if (storeId != null) { + sc.setParameters(STORE_ID, new Long[]{storeId}); + } else if (zoneId != null) { + List imageStores = imageStoreDao.listStoresByZoneId(zoneId); + Object[] imageStoreIds = imageStores.stream().map(ImageStoreVO::getId).toArray(); + sc.setParameters(STORE_ID, imageStoreIds); + } List snapshotList = listBy(sc, new Filter(SnapshotDataStoreVO.class, CREATED, false, null, null)); - if (CollectionUtils.isNotEmpty(snapshotList)) { - return snapshotList.get(0); + if (CollectionUtils.isEmpty(snapshotList)) { + return null; + } + + SnapshotDataStoreVO parent = snapshotList.get(0); + + if (kvmIncrementalSnapshot && parent.getKvmCheckpointPath() == null && Hypervisor.HypervisorType.KVM.equals(hypervisorType)) { + return null; + } + + return parent; + } + + @Override + public SnapshotDataStoreVO findBySnapshotIdAndDataStoreRoleAndState(long snapshotId, DataStoreRole role, State state) { + SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + sc.setParameters(STATE, state); + return findOneBy(sc); + } + + @Override + public SnapshotDataStoreVO findOneBySnapshotId(long snapshotId, long zoneId) { + try (TransactionLegacy transactionLegacy = TransactionLegacy.currentTxn()) { + try (PreparedStatement preparedStatement = transactionLegacy.prepareStatement(FIND_SNAPSHOT_IN_ZONE)) { + preparedStatement.setLong(1, snapshotId); + preparedStatement.setLong(2, zoneId); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + return toEntityBean(resultSet, false); + } + } + } + } catch (SQLException e) { + logger.warn(String.format("Failed to find %s snapshot in zone %s due to [%s].", snapshotId, zoneId, e.getMessage()), e); } return null; } @Override - public List listBySnapshot(long snapshotId, DataStoreRole role) { + public List listBySnapshotId(long snapshotId) { + SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + + @Override + public List listBySnapshotAndDataStoreRole(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); return listBy(sc); } + @Override + public List listSnapshotsBySnapshotId(long snapshotId) { + SearchCriteria sc = searchBySnapshotId.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + return listBy(sc); + } + @Override public List listReadyBySnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); @@ -314,6 +424,14 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase listReadyBySnapshotId(long snapshotId) { + SearchCriteria sc = searchBySnapshotId.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STATE, State.Ready); + return listBy(sc); + } + @Override public SnapshotDataStoreVO findBySourceSnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); @@ -321,6 +439,12 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = createSearchCriteriaBySnapshotIdAndStoreRole(snapshotId, role); + return findOneBy(sc); + } + @Override public List listAllByVolumeAndDataStore(long volumeId, DataStoreRole role) { SearchCriteria sc = searchFilteringStoreIdEqStateEqStoreRoleEqIdEqUpdateCountEqSnapshotIdEqVolumeIdEq.create(); @@ -340,9 +464,17 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase findBySnapshotId(long snapshotId) { - SearchCriteria sc = idStateNeqSearch.create(); + SearchCriteria sc = idStateNinSearch.create(); sc.setParameters(SNAPSHOT_ID, snapshotId); - sc.setParameters(STATE, State.Destroyed); + sc.setParameters(STATE, State.Destroyed.name()); + return listBy(sc); + } + + @Override + public List findBySnapshotIdAndNotInDestroyedHiddenState(long snapshotId) { + SearchCriteria sc = idStateNinSearch.create(); + sc.setParameters(SNAPSHOT_ID, snapshotId); + sc.setParameters(STATE, State.Destroyed.name(), State.Hidden.name()); return listBy(sc); } @@ -485,13 +617,35 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = snapshotVOSearch.create(); sc.setParameters(VOLUME_ID, volumeId); SnapshotVO snapshot = snapshotDao.findOneBy(sc); - return snapshot != null && HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(snapshot.getHypervisorType()); + if (snapshot == null) { + return false; + } + + Hypervisor.HypervisorType hypervisorType = snapshot.getHypervisorType(); + return HYPERVISORS_SUPPORTING_SNAPSHOTS_CHAINING.contains(hypervisorType) || (Hypervisor.HypervisorType.KVM.equals(hypervisorType) && kvmIncrementalSnapshot); + } + + @Override + public List listReadyByVolumeIdAndCheckpointPathNotNull(long volumeId) { + SearchCriteria sc = searchFilteringStoreIdInVolumeIdEqStoreRoleEqStateEqKVMCheckpointNotNull.create(); + sc.setParameters(VOLUME_ID, volumeId); + sc.setParameters(STATE, State.Ready); + return listBy(sc); + } + + @Override + public List listExtractedSnapshotsBeforeDate(Date beforeDate) { + SearchCriteria sc = searchFilterStateAndDownloadUrlNotNullAndDownloadUrlCreatedBefore.create(); + sc.setParameters(URL_CREATED_BEFORE, beforeDate); + sc.setParameters(STATE, State.Ready); + + return listBy(sc); } @Override diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java index 7a466c1f505..44eb7e6c02c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -80,12 +82,18 @@ public class SnapshotDataStoreVO implements StateObject { @Override diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml index d6d72f9228e..d308a9e5aaf 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-common-daos-between-management-and-usage-context.xml @@ -62,7 +62,8 @@ - + + diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 4f22234d7bf..7e9d6c3b54f 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -138,6 +138,7 @@ + @@ -270,6 +271,7 @@ + @@ -300,4 +302,11 @@ + + + + + + + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql index c36b71c2f25..eec4ac3f028 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql @@ -55,7 +55,7 @@ UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Clou AND system_use = 1 AND ram_size < 512; -- NSX Plugin -- -CREATE TABLE `cloud`.`nsx_providers` ( +CREATE TABLE IF NOT EXISTS `cloud`.`nsx_providers` ( `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', `uuid` varchar(40), `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql index bf13e5eee1a..3dd6c18f57c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42000to42010.sql @@ -19,6 +19,12 @@ -- Schema upgrade from 4.20.0.0 to 4.20.1.0 --; +-- Delete user vm details for guest CPU mode/model which are root admin only +DELETE FROM `cloud`.`user_vm_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model'); + +-- Delete template details for guest CPU mode/model which are root admin only +DELETE FROM `cloud`.`vm_template_details` WHERE `name` IN ('guest.cpu.mode','guest.cpu.model'); + -- Add column api_key_access to user and account tables CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" '); @@ -47,3 +53,81 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.storage_pool', 'used_iops', 'bigint -- Add reason column for op_ha_work CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.op_ha_work', 'reason', 'varchar(32) DEFAULT NULL COMMENT "Reason for the HA work"'); + +-- Support for XCP-ng 8.3.0 and XenServer 8.4 by adding hypervisor capabilities +-- https://docs.xenserver.com/en-us/xenserver/8/system-requirements/configuration-limits.html +-- https://docs.xenserver.com/en-us/citrix-hypervisor/system-requirements/configuration-limits.html +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hypervisor_version, max_guests_limit, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported) VALUES (UUID(), 'XenServer', '8.3.0', 1000, 254, 64, 1); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(uuid, hypervisor_type, hypervisor_version, max_guests_limit, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported) VALUES (UUID(), 'XenServer', '8.4.0', 1000, 240, 64, 1); + +-- Add missing and new Guest OS mappings +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 10 (64-bit)', 'XenServer', '8.2.1', 'Debian Buster 10'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 15 (64-bit)', 'XenServer', '8.2.1', 'SUSE Linux Enterprise 15 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2022 (64-bit)', 'XenServer', '8.2.1', 'Windows Server 2022 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows 11 (64-bit)', 'XenServer', '8.2.1', 'Windows 11'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 20.04 LTS', 'XenServer', '8.2.1', 'Ubuntu Focal Fossa 20.04'); + +-- Copy XS 8.2.1 hypervisor guest OS mappings to XS 8.3 and 8.3 mappings to 8.4 +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'Xenserver', '8.3.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='Xenserver' AND hypervisor_version='8.2.1'; + +-- Add new and missing guest os mappings for XS 8.3 +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'Rocky Linux 9', 'XenServer', '8.3.0', 'Rocky Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'Rocky Linux 8', 'XenServer', '8.3.0', 'Rocky Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'AlmaLinux 9', 'XenServer', '8.3.0', 'AlmaLinux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'AlmaLinux 8', 'XenServer', '8.3.0', 'AlmaLinux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 12 (64-bit)', 'XenServer', '8.3.0', 'Debian Bookworm 12'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (3, 'Oracle Linux 9', 'XenServer', '8.3.0', 'Oracle Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (3, 'Oracle Linux 8', 'XenServer', '8.3.0', 'Oracle Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Red Hat Enterprise Linux 8.0', 'XenServer', '8.3.0', 'Red Hat Enterprise Linux 8'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Red Hat Enterprise Linux 9.0', 'XenServer', '8.3.0', 'Red Hat Enterprise Linux 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 22.04 LTS', 'XenServer', '8.3.0', 'Ubuntu Jammy Jellyfish 22.04'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 12 SP5 (64-bit)', 'XenServer', '8.3.0', 'SUSE Linux Enterprise Server 12 SP5 (64-bit'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'NeoKylin Linux Server 7', 'XenServer', '8.3.0', 'NeoKylin Linux Server 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 9', 'XenServer', '8.3.0', 'CentOS Stream 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Scientific Linux 7', 'XenServer', '8.3.0', 'Scientific Linux 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (7, 'Generic Linux UEFI', 'XenServer', '8.3.0', 'Generic Linux UEFI'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (7, 'Generic Linux BIOS', 'XenServer', '8.3.0', 'Generic Linux BIOS'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Gooroom Platform 2.0', 'XenServer', '8.3.0', 'Gooroom Platform 2.0'); + +INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) SELECT UUID(),'Xenserver', '8.4.0', guest_os_name, guest_os_id, utc_timestamp(), 0 FROM `cloud`.`guest_os_hypervisor` WHERE hypervisor_type='Xenserver' AND hypervisor_version='8.3.0'; + +-- Add new guest os mappings for XS 8.4 and KVM +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025', 'XenServer', '8.4.0', 'Windows Server 2025'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 24.04 LTS', 'XenServer', '8.4.0', 'Ubuntu Noble Numbat 24.04'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 10 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 10 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 11 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 11 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Debian GNU/Linux 12 (64-bit)', 'KVM', 'default', 'Debian GNU/Linux 12 (64-bit)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows 11 (64-bit)', 'KVM', 'default', 'Windows 11'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (6, 'Windows Server 2025', 'KVM', 'default', 'Windows Server 2025'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (10, 'Ubuntu 24.04 LTS', 'KVM', 'default', 'Ubuntu 24.04 LTS'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 10 (preview)', 'XenServer', '8.4.0', 'CentOS Stream 10 (preview)'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (1, 'CentOS Stream 9', 'XenServer', '8.4.0', 'CentOS Stream 9'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'Scientific Linux 7', 'XenServer', '8.4.0', 'Scientific Linux 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (4, 'NeoKylin Linux Server 7', 'XenServer', '8.4.0', 'NeoKylin Linux Server 7'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (5, 'SUSE Linux Enterprise Server 12 SP5 (64-bit)', 'XenServer', '8.4.0', 'SUSE Linux Enterprise Server 12 SP5 (64-bit'); +CALL ADD_GUEST_OS_AND_HYPERVISOR_MAPPING (2, 'Gooroom Platform 2.0', 'XenServer', '8.4.0', 'Gooroom Platform 2.0'); + +-- Grant access to 2FA APIs for the "Read-Only User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support User - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Read-Only Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); + +-- Grant access to 2FA APIs for the "Support Admin - Default" role + +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); +CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); + +-- Re-apply VPC: update default network offering for vpc tier to conserve_mode=1 (#8309) +UPDATE `cloud`.`network_offerings` SET conserve_mode=1 WHERE name='DefaultIsolatedNetworkOfferingForVpcNetworks'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql index b01243ad989..167dd92730c 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100.sql @@ -19,9 +19,13 @@ -- Schema upgrade from 4.20.1.0 to 4.21.0.0 --; --- Add columns max_backup and backup_interval_type to backup table -ALTER TABLE `cloud`.`backup_schedule` ADD COLUMN `max_backups` int(8) default NULL COMMENT 'maximum number of backups to maintain'; -ALTER TABLE `cloud`.`backups` ADD COLUMN `backup_interval_type` int(5) COMMENT 'type of backup, e.g. manual, recurring - hourly, daily, weekly or monthly'; +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'max_backups', 'INT(8) UNSIGNED NOT NULL DEFAULT 0 COMMENT ''Maximum number of backups to be retained'''); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backup_schedule_id', 'BIGINT(20) UNSIGNED'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'quiescevm', 'tinyint(1) default NULL COMMENT "Quiesce VM before taking backup"'); + +-- Update default value for the config 'vm.network.nic.max.secondary.ipaddresses' (and value to default value if value is null) +UPDATE `cloud`.`configuration` SET default_value = '10' WHERE name = 'vm.network.nic.max.secondary.ipaddresses'; +UPDATE `cloud`.`configuration` SET value = '10' WHERE name = 'vm.network.nic.max.secondary.ipaddresses' AND value IS NULL; -- Add console_endpoint_creator_address column to cloud.console_session table CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'console_endpoint_creator_address', 'VARCHAR(45)'); @@ -34,6 +38,722 @@ INSERT INTO `cloud`.`role_permissions` (uuid, role_id, rule, permission, sort_or SELECT uuid(), role_id, 'quotaCreditsList', permission, sort_order FROM `cloud`.`role_permissions` rp WHERE rp.rule = 'quotaStatement' -AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList'); + AND NOT EXISTS(SELECT 1 FROM cloud.role_permissions rp_ WHERE rp.role_id = rp_.role_id AND rp_.rule = 'quotaCreditsList'); CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'last_mgmt_server_id', 'bigint unsigned DEFAULT NULL COMMENT "last management server this host is connected to" AFTER `mgmt_server_id`'); + +----------------------------------------------------------- +-- CKS Enhancements: +----------------------------------------------------------- +-- Add for_cks column to the vm_template table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the template can be used for CKS cluster deployment"'); + +-- Add support for different node types service offerings on CKS clusters +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Control Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for Worker Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_service_offering_id', 'bigint unsigned COMMENT "service offering ID for etcd Nodes"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_count', 'bigint unsigned COMMENT "number of etcd nodes to be deployed for the Kubernetes cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','control_node_template_id', 'bigint unsigned COMMENT "template id to be used for Control Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','worker_node_template_id', 'bigint unsigned COMMENT "template id to be used for Worker Node(s)"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','etcd_node_template_id', 'bigint unsigned COMMENT "template id to be used for etcd Nodes"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_id', 'bigint unsigned COMMENT "user data id representing the associated cni configuration"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster','cni_config_details', 'varchar(4096) DEFAULT NULL COMMENT "user data details representing the values required for the cni configuration associated"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','etcd_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the VM is an etcd node"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','external_node', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node was imported into the Kubernetes cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','manual_upgrade', 'tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT "indicates if the node is marked for manual upgrade and excluded from the Kubernetes cluster upgrade operation"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.kubernetes_cluster_vm_map','kubernetes_node_version', 'varchar(40) COMMENT "version of k8s the cluster node is on"'); + +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_node_service_offering_id` FOREIGN KEY `fk_cluster__control_node_service_offering_id`(`control_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_node_service_offering_id` FOREIGN KEY `fk_cluster__worker_node_service_offering_id`(`worker_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_node_service_offering_id` FOREIGN KEY `fk_cluster__etcd_node_service_offering_id`(`etcd_node_service_offering_id`) REFERENCES `service_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__control_node_template_id` FOREIGN KEY `fk_cluster__control_node_template_id`(`control_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__worker_node_template_id` FOREIGN KEY `fk_cluster__worker_node_template_id`(`worker_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_node_template_id` FOREIGN KEY `fk_cluster__etcd_node_template_id`(`etcd_node_template_id`) REFERENCES `vm_template`(`id`) ON DELETE CASCADE; + +-- Add for_cks column to the user_data table to represent CNI Configuration stored as userdata +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the user data represent CNI configuration meant for CKS use only"'); + +-- Add use VR IP as resolver option on VPC +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc','use_router_ip_resolver', 'tinyint(1) DEFAULT 0 COMMENT "use router ip as resolver instead of dns options"'); +----------------------------------------------------------- +-- END - CKS Enhancements +----------------------------------------------------------- + +-- Add table for reconcile commands +CREATE TABLE IF NOT EXISTS `cloud`.`reconcile_commands` ( + `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT, + `management_server_id` bigint unsigned NOT NULL COMMENT 'node id of the management server', + `host_id` bigint unsigned NOT NULL COMMENT 'id of the host', + `request_sequence` bigint unsigned NOT NULL COMMENT 'sequence of the request', + `resource_id` bigint unsigned DEFAULT NULL COMMENT 'id of the resource', + `resource_type` varchar(255) COMMENT 'type if the resource', + `state_by_management` varchar(255) COMMENT 'state of the command updated by management server', + `state_by_agent` varchar(255) COMMENT 'state of the command updated by cloudstack agent', + `command_name` varchar(255) COMMENT 'name of the command', + `command_info` MEDIUMTEXT COMMENT 'info of the command', + `answer_name` varchar(255) COMMENT 'name of the answer', + `answer_info` MEDIUMTEXT COMMENT 'info of the answer', + `created` datetime COMMENT 'date the reconcile command was created', + `removed` datetime COMMENT 'date the reconcile command was removed', + `updated` datetime COMMENT 'date the reconcile command was updated', + `retry_count` bigint unsigned DEFAULT 0 COMMENT 'The retry count of reconciliation', + PRIMARY KEY(`id`), + INDEX `i_reconcile_command__host_id`(`host_id`), + CONSTRAINT `fk_reconcile_command__host_id` FOREIGN KEY (`host_id`) REFERENCES `host`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +--- KVM Incremental Snapshots + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'kvm_checkpoint_path', 'varchar(255)'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.snapshot_store_ref', 'end_of_chain', 'int(1) unsigned'); + +-- Create table storage_pool_and_access_group_map +CREATE TABLE IF NOT EXISTS `cloud`.`storage_pool_and_access_group_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `pool_id` bigint(20) unsigned NOT NULL COMMENT "pool id", + `storage_access_group` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `fk_storage_pool_and_access_group_map__pool_id` (`pool_id`), + CONSTRAINT `fk_storage_pool_and_access_group_map__pool_id` FOREIGN KEY (`pool_id`) REFERENCES `storage_pool` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the host"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.cluster', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the cluster"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_pod_ref', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the pod"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.data_center', 'storage_access_groups', 'varchar(255) DEFAULT NULL COMMENT "storage access groups for the hosts in the zone"'); + +-- Add featured, sort_key, created, removed columns for guest_os_category +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'featured', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "whether the category is featured or not" AFTER `uuid`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'sort_key', 'int NOT NULL DEFAULT 0 COMMENT "sort key used for customising sort method" AFTER `featured`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'created', 'datetime COMMENT "date on which the category was created" AFTER `sort_key`'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.guest_os_category', 'removed', 'datetime COMMENT "date removed if not null" AFTER `created`'); + +-- Begin: Changes for Guest OS category cleanup +-- Add new OS categories if not present +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`; +CREATE PROCEDURE `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`(IN os_name VARCHAR(255)) +BEGIN + IF NOT EXISTS ((SELECT 1 FROM `cloud`.`guest_os_category` WHERE name = os_name)) + THEN + INSERT INTO `cloud`.`guest_os_category` (name, uuid) + VALUES (os_name, UUID()) +; END IF +; END; + +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Fedora'); +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('Rocky Linux'); +CALL `cloud`.`INSERT_CATEGORY_IF_NOT_EXIST`('AlmaLinux'); + +-- Move existing guest OS to new categories +DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`; +CREATE PROCEDURE `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`(IN category_name VARCHAR(255), IN os_name VARCHAR(255)) +BEGIN + DECLARE category_id BIGINT +; SELECT `id` INTO category_id + FROM `cloud`.`guest_os_category` + WHERE `name` = category_name + LIMIT 1 +; IF category_id IS NULL THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Category not found' +; END IF +; UPDATE `cloud`.`guest_os` + SET `category_id` = category_id + WHERE `display_name` LIKE CONCAT('%', os_name, '%') +; END; +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Rocky Linux', 'Rocky Linux'); +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('AlmaLinux', 'AlmaLinux'); +CALL `cloud`.`UPDATE_CATEGORY_FOR_GUEST_OSES`('Fedora', 'Fedora'); + +-- Move existing guest OS whose category will be deleted to Other category +DROP PROCEDURE IF EXISTS `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`; +CREATE PROCEDURE `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`(IN to_category_name VARCHAR(255), IN from_category_name VARCHAR(255)) +BEGIN + DECLARE done INT DEFAULT 0 +; DECLARE to_category_id BIGINT +; SELECT id INTO to_category_id + FROM `cloud`.`guest_os_category` + WHERE `name` = to_category_name + LIMIT 1 +; IF to_category_id IS NULL THEN + SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'ToCategory not found' +; END IF +; UPDATE `cloud`.`guest_os` + SET `category_id` = to_category_id + WHERE `category_id` = (SELECT `id` FROM `cloud`.`guest_os_category` WHERE `name` = from_category_name) +; UPDATE `cloud`.`guest_os_category` SET `removed`=now() WHERE `name` = from_category_name +; END; +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Novel'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'None'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Unix'); +CALL `cloud`.`UPDATE_NEW_AND_DELETE_OLD_CATEGORY_FOR_GUEST_OS`('Other', 'Mac'); + +-- Update featured for existing guest OS categories +UPDATE `cloud`.`guest_os_category` SET featured = 1; + +-- Update sort order for all guest OS categories +UPDATE `cloud`.`guest_os_category` +SET `sort_key` = CASE + WHEN `name` = 'Ubuntu' THEN 1 + WHEN `name` = 'Debian' THEN 2 + WHEN `name` = 'Fedora' THEN 3 + WHEN `name` = 'CentOS' THEN 4 + WHEN `name` = 'Rocky Linux' THEN 5 + WHEN `name` = 'AlmaLinux' THEN 6 + WHEN `name` = 'Oracle' THEN 7 + WHEN `name` = 'RedHat' THEN 8 + WHEN `name` = 'SUSE' THEN 9 + WHEN `name` = 'Windows' THEN 10 + WHEN `name` = 'Other' THEN 11 + ELSE `sort_key` +END; +-- End: Changes for Guest OS category cleanup + +-- Update description for configuration: host.capacityType.to.order.clusters +UPDATE `cloud`.`configuration` SET + `description` = 'The host capacity type (CPU, RAM or COMBINED) is used by deployment planner to order clusters during VM resource allocation' +WHERE `name` = 'host.capacityType.to.order.clusters' + AND `description` = 'The host capacity type (CPU or RAM) is used by deployment planner to order clusters during VM resource allocation'; + +-- Whitelabel GUI +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `uuid` varchar(255) UNIQUE, + `name` varchar(2048) NOT NULL COMMENT 'A name to identify the theme.', + `description` varchar(4096) DEFAULT NULL COMMENT 'A description for the theme.', + `css` text DEFAULT NULL COMMENT 'The CSS to be retrieved and imported into the GUI when matching the theme access configurations.', + `json_configuration` text DEFAULT NULL COMMENT 'The JSON with the configurations to be retrieved and imported into the GUI when matching the theme access configurations.', + `recursive_domains` tinyint(1) DEFAULT 0 COMMENT 'Defines whether the subdomains of the informed domains are considered. Default value is false.', + `is_public` tinyint(1) default 1 COMMENT 'Defines whether a theme can be retrieved by anyone when only the `internet_domains_names` is informed. If the `domain_uuids` or `account_uuids` is informed, it is considered as `false`.', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE IF NOT EXISTS `cloud`.`gui_themes_details` ( + `id` bigint(20) unsigned NOT NULL auto_increment, + `gui_theme_id` bigint(20) unsigned NOT NULL COMMENT 'Foreign key referencing the GUI theme on `gui_themes` table.', + `type` varchar(100) NOT NULL COMMENT 'The type of GUI theme details. Valid options are: `account`, `domain` and `commonName`', + `value` text NOT NULL COMMENT 'The value of the `type` details. Can be an UUID (account or domain) or internet common name.', + PRIMARY KEY (`id`), + CONSTRAINT `fk_gui_themes_details__gui_theme_id` FOREIGN KEY (`gui_theme_id`) REFERENCES `gui_themes`(`id`) +); + +-- Create the GPU card table to hold the GPU card information +CREATE TABLE IF NOT EXISTS `cloud`.`gpu_card` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `device_id` varchar(4) NOT NULL COMMENT 'device id of the GPU card', + `device_name` varchar(255) NOT NULL COMMENT 'device name of the GPU card', + `name` varchar(255) NOT NULL COMMENT 'name of the GPU card', + `vendor_name` varchar(255) NOT NULL COMMENT 'vendor name of the GPU card', + `vendor_id` varchar(4) NOT NULL COMMENT 'vendor id of the GPU card', + `created` datetime NOT NULL COMMENT 'date created', + PRIMARY KEY (`id`), + UNIQUE KEY (`vendor_id`, `device_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GPU cards supported by CloudStack'; + +-- Create the vGPU profile table to hold the vGPU profile information. +CREATE TABLE IF NOT EXISTS `cloud`.`vgpu_profile` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL COMMENT 'name of the vGPU profile', + `description` varchar(255) DEFAULT NULL COMMENT 'description of the vGPU profile', + `card_id` bigint unsigned NOT NULL COMMENT 'id of the GPU card', + `video_ram` bigint unsigned DEFAULT NULL COMMENT 'video RAM of the vGPU profile', + `max_heads` bigint unsigned DEFAULT NULL COMMENT 'maximum number of heads of the vGPU profile', + `max_resolution_x` bigint unsigned DEFAULT NULL COMMENT 'maximum resolution x of the vGPU profile', + `max_resolution_y` bigint unsigned DEFAULT NULL COMMENT 'maximum resolution y of the vGPU profile', + `max_vgpu_per_pgpu` bigint unsigned DEFAULT NULL COMMENT 'Maximum number of vGPUs per physical GPU', + `created` datetime NOT NULL COMMENT 'date created', + PRIMARY KEY (`id`), + UNIQUE KEY (`name`, `card_id`), + CONSTRAINT `fk_vgpu_profile_card_id` FOREIGN KEY (`card_id`) REFERENCES `gpu_card`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='vGPU profiles supported by CloudStack'; + +-- Create the GPU device table to hold the GPU device information on different hosts +CREATE TABLE IF NOT EXISTS `cloud`.`gpu_device` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `uuid` varchar(40) NOT NULL UNIQUE, + `card_id` bigint unsigned NOT NULL COMMENT 'id of the GPU card', + `vgpu_profile_id` bigint unsigned DEFAULT NULL COMMENT 'id of the vGPU profile.', + `bus_address` varchar(255) NOT NULL COMMENT 'PCI bus address of the GPU device', + `type` varchar(32) NOT NULL COMMENT 'type of the GPU device. PCI or MDEV', + `host_id` bigint unsigned NOT NULL COMMENT 'id of the host where GPU is installed', + `vm_id` bigint unsigned DEFAULT NULL COMMENT 'id of the VM using this GPU device', + `numa_node` varchar(255) DEFAULT NULL COMMENT 'NUMA node of the GPU device', + `pci_root` varchar(255) DEFAULT NULL COMMENT 'PCI root of the GPU device', + `parent_gpu_device_id` bigint unsigned DEFAULT NULL COMMENT 'id of the parent GPU device. null if it is a physical GPU device and for vGPUs points to the actual GPU', + `state` varchar(32) NOT NULL COMMENT 'state of the GPU device', + `managed_state` varchar(32) NOT NULL COMMENT 'resource state of the GPU device', + PRIMARY KEY (`id`), + UNIQUE KEY (`bus_address`, `host_id`), + CONSTRAINT `fk_gpu_devices__card_id` FOREIGN KEY (`card_id`) REFERENCES `gpu_card` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_gpu_devices__host_id` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_gpu_devices__vm_id` FOREIGN KEY (`vm_id`) REFERENCES `vm_instance` (`id`) ON DELETE SET NULL, + CONSTRAINT `fk_gpu_devices__parent_gpu_device_id` FOREIGN KEY (`parent_gpu_device_id`) REFERENCES `gpu_device` (`id`) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GPU devices installed on hosts'; + +-- Add references to GPU tables +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'vgpu_profile_id', 'bigint unsigned DEFAULT NULL COMMENT "vgpu profile ID"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'gpu_count', 'int unsigned DEFAULT NULL COMMENT "number of GPUs"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.service_offering', 'gpu_display', 'boolean DEFAULT false COMMENT "enable GPU display"'); +CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.service_offering','fk_service_offering__vgpu_profile_id'); +CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.service_offering', 'fk_service_offering__vgpu_profile_id', '(vgpu_profile_id)', '`vgpu_profile`(`id`)'); + +-- Netris Plugin +CREATE TABLE `cloud`.`netris_providers` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `name` varchar(40), + `url` varchar(255) NOT NULL, + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `site_name` varchar(255) NOT NULL, + `tenant_name` varchar(255) NOT NULL, + `netris_tag` varchar(255) NOT NULL, + `created` datetime NOT NULL COMMENT 'created date', + `removed` datetime COMMENT 'removed date if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_netris_providers__zone_id` FOREIGN KEY `fk_netris_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_netris_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Drop the Tungsten and NSX columns from the network offerings (replaced by checking the provider on the ntwk_offering_service_map table) +ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_tungsten`; +ALTER TABLE `cloud`.`network_offerings` DROP COLUMN `for_nsx`; + +-- Drop the Tungsten and NSX columns from the VPC offerings (replaced by checking the provider on the vpc_offering_service_map table) +ALTER TABLE `cloud`.`vpc_offerings` DROP COLUMN `for_nsx`; + +-- Add next_hop to the static_routes table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.static_routes', 'next_hop', 'varchar(50) COMMENT "next hop of the static route" AFTER `vpc_gateway_id`'); + +-- Add `for_router` to `user_ip_address` table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_ip_address', 'for_router', 'tinyint(1) DEFAULT 0 COMMENT "True if the ip address is used by Domain Router to expose services"'); + +-- Add Netris Autoscaling rules +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'cpu', 'VM CPU - average percentage', 'vm.cpu.average.percentage', NOW()); +INSERT IGNORE INTO `cloud`.`counter` (uuid, provider, source, name, value, created) VALUES (UUID(), 'Netris', 'memory', 'VM Memory - average percentage', 'vm.memory.average.percentage', NOW()); + +-- Rename user_vm_details to vm_instance_details +ALTER TABLE `cloud`.`user_vm_details` RENAME TO `cloud`.`vm_instance_details`; +ALTER TABLE `cloud`.`vm_instance_details` DROP FOREIGN KEY `fk_user_vm_details__vm_id`; +ALTER TABLE `cloud`.`vm_instance_details` ADD CONSTRAINT `fk_vm_instance_details__vm_id` FOREIGN KEY (vm_id) REFERENCES vm_instance(id) ON DELETE CASCADE; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backup_schedule', 'uuid', 'VARCHAR(40) NOT NULL'); +UPDATE `cloud`.`backup_schedule` SET uuid = UUID(); + +-- Extension framework +UPDATE `cloud`.`configuration` SET value = CONCAT(value, ',External') WHERE name = 'hypervisor.list'; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `type` varchar(255) NOT NULL COMMENT 'Type of the extension: Orchestrator, etc', + `relative_path` varchar(2048) NOT NULL COMMENT 'Path for the extension relative to the root extensions directory', + `path_ready` tinyint(1) DEFAULT '0' COMMENT 'True if the extension path is in ready state across management servers', + `is_user_defined` tinyint(1) DEFAULT '0' COMMENT 'True if the extension is added by admin', + `state` char(32) NOT NULL COMMENT 'State of the extension - Enabled or Disabled', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_id` bigint unsigned NOT NULL COMMENT 'extension to which the detail is related to', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_details__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `extension` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_id` bigint(20) unsigned NOT NULL, + `resource_id` bigint(20) unsigned NOT NULL, + `resource_type` char(255) NOT NULL, + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE, + INDEX `idx_extension_resource` (`resource_id`, `resource_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_resource_map_details` ( + `id` bigint unsigned UNIQUE NOT NULL AUTO_INCREMENT COMMENT 'id', + `extension_resource_map_id` bigint unsigned NOT NULL COMMENT 'mapping to which the detail is related', + `name` varchar(255) NOT NULL COMMENT 'name of the detail', + `value` varchar(255) NOT NULL COMMENT 'value of the detail', + `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_resource_map_details__map_id` FOREIGN KEY (`extension_resource_map_id`) + REFERENCES `extension_resource_map` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(255) NOT NULL UNIQUE, + `name` varchar(255) NOT NULL, + `description` varchar(4096), + `extension_id` bigint(20) unsigned NOT NULL, + `resource_type` varchar(255), + `allowed_role_types` int unsigned NOT NULL DEFAULT '1', + `success_message` varchar(4096), + `error_message` varchar(4096), + `enabled` boolean DEFAULT true, + `timeout` int unsigned NOT NULL DEFAULT '5' COMMENT 'The timeout in seconds to wait for the action to complete before failing', + `created` datetime NOT NULL, + `removed` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_extension_custom_action__extension_id` FOREIGN KEY (`extension_id`) + REFERENCES `cloud`.`extension`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `cloud`.`extension_custom_action_details` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `extension_custom_action_id` bigint(20) unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + CONSTRAINT `fk_custom_action_details__action_id` FOREIGN KEY (`extension_custom_action_id`) + REFERENCES `cloud`.`extension_custom_action`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_template', 'extension_id', 'bigint unsigned DEFAULT NULL COMMENT "id of the extension"'); + +-- Add built-in Extensions and Custom Actions + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN ext_desc VARCHAR(255), + IN ext_path VARCHAR(255) +) +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension` WHERE `name` = ext_name + ) THEN + INSERT INTO `cloud`.`extension` ( + `uuid`, `name`, `description`, `type`, + `relative_path`, `path_ready`, + `is_user_defined`, `state`, `created`, `removed` + ) + VALUES ( + UUID(), ext_name, ext_desc, 'Orchestrator', + ext_path, 1, 0, 'Enabled', NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN detail_key VARCHAR(255), + IN detail_value TEXT, + IN display TINYINT(1) +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_details` + WHERE `extension_id` = ext_id AND `name` = detail_key + ) THEN + INSERT INTO `cloud`.`extension_details` ( + `extension_id`, `name`, `value`, `display` + ) + VALUES ( + ext_id, detail_key, detail_value, display + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('Proxmox', 'Sample extension for Proxmox written in bash', 'Proxmox/proxmox.sh'); +CALL `cloud`.`INSERT_EXTENSION_DETAIL_IF_NOT_EXISTS`('Proxmox', 'orchestratorrequirespreparevm', 'true', 0); + +CALL `cloud`.`INSERT_EXTENSION_IF_NOT_EXISTS`('HyperV', 'Sample extension for HyperV written in python', 'HyperV/hyperv.py'); + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN action_desc VARCHAR(4096), + IN resource_type VARCHAR(255), + IN allowed_roles INT UNSIGNED, + IN success_msg VARCHAR(4096), + IN error_msg VARCHAR(4096), + IN timeout_seconds INT UNSIGNED +) +BEGIN + DECLARE ext_id BIGINT +; SELECT `id` INTO ext_id FROM `cloud`.`extension` WHERE `name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action` WHERE `name` = action_name AND `extension_id` = ext_id + ) THEN + INSERT INTO `cloud`.`extension_custom_action` ( + `uuid`, `name`, `description`, `extension_id`, `resource_type`, + `allowed_role_types`, `success_message`, `error_message`, + `enabled`, `timeout`, `created`, `removed` + ) + VALUES ( + UUID(), action_name, action_desc, ext_id, resource_type, + allowed_roles, success_msg, error_msg, + 1, timeout_seconds, NOW(), NULL + ) +; END IF +;END; + +DROP PROCEDURE IF EXISTS `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`; +CREATE PROCEDURE `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS` ( + IN ext_name VARCHAR(255), + IN action_name VARCHAR(255), + IN param_json TEXT +) +BEGIN + DECLARE action_id BIGINT UNSIGNED +; SELECT `eca`.`id` INTO action_id FROM `cloud`.`extension_custom_action` `eca` + JOIN `cloud`.`extension` `e` ON `e`.`id` = `eca`.`extension_id` + WHERE `eca`.`name` = action_name AND `e`.`name` = ext_name LIMIT 1 +; IF NOT EXISTS ( + SELECT 1 FROM `cloud`.`extension_custom_action_details` + WHERE `extension_custom_action_id` = action_id + AND `name` = 'parameters' + ) THEN + INSERT INTO `cloud`.`extension_custom_action_details` ( + `extension_custom_action_id`, + `name`, + `value`, + `display` + ) VALUES ( + action_id, + 'parameters', + param_json, + 0 + ) +; END IF +;END; + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'ListSnapshots', 'List Instance snapshots', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'CreateSnapshot', 'Create an Instance snapshot', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'RestoreSnapshot', 'Restore Instance to the specific snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('Proxmox', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'CreateSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + }, + { + "name": "snap_description", + "type": "STRING", + "validationformat": "NONE", + "required": false + }, + { + "name": "snap_save_memory", + "type": "BOOLEAN", + "validationformat": "NONE", + "required": false + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'RestoreSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'Proxmox', + 'DeleteSnapshot', + '[ + { + "name": "snap_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'ListSnapshots', 'List checkpoints/snapshots for the Instance', 'VirtualMachine', 15, 'Snapshots fetched for {{resourceName}} in {{extensionName}}', 'List Snapshots failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'CreateSnapshot', 'Create a checkpoint/snapshot for the Instance', 'VirtualMachine', 15, 'Snapshot created for {{resourceName}} in {{extensionName}}', 'Snapshot creation failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'RestoreSnapshot', 'Restore Instance to the specified snapshot', 'VirtualMachine', 15, 'Successfully restored snapshot for {{resourceName}} in {{extensionName}}', 'Restore snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'DeleteSnapshot', 'Delete the specified snapshot', 'VirtualMachine', 15, 'Successfully deleted snapshot for {{resourceName}} in {{extensionName}}', 'Delete snapshot failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Suspend', 'Suspend the Instance by freezing its current state in RAM', 'VirtualMachine', 15, 'Successfully suspended {{resourceName}} in {{extensionName}}', 'Suspend failed for {{resourceName}}', 60); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_IF_NOT_EXISTS`('HyperV', 'Resume', 'Resumes a suspended Instance, restoring CPU execution from memory.', 'VirtualMachine', 15, 'Successfully resumed {{resourceName}} in {{extensionName}}', 'Resume failed for {{resourceName}}', 60); + +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'ListSnapshots', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'CreateSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'RestoreSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'DeleteSnapshot', + '[ + { + "name": "snapshot_name", + "type": "STRING", + "validationformat": "NONE", + "required": true + } + ]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Suspend', + '[]' +); +CALL `cloud`.`INSERT_EXTENSION_CUSTOM_ACTION_DETAILS_IF_NOT_EXISTS`( + 'HyperV', + 'Resume', + '[]' +); + +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `cidr` varchar(255) DEFAULT NULL COMMENT 'CloudStack managed vms get IP address from cidr.In general this cidr also serves as the network CIDR. But in case IP reservation feature is being used by a Guest network, networkcidr is the Effective network CIDR for that network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `gateway` varchar(255) DEFAULT NULL COMMENT 'gateway(s) for this network configuration'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_cidr` varchar(1024) DEFAULT NULL COMMENT 'IPv6 cidr(s) for this network'; +ALTER TABLE `cloud`.`networks` MODIFY COLUMN `ip6_gateway` varchar(1024) DEFAULT NULL COMMENT 'IPv6 gateway(s) for this network'; + +-- Add columns name, description and backup_interval_type to backup table +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'name', 'VARCHAR(255) NULL COMMENT "name of the backup"'); +UPDATE `cloud`.`backups` backup INNER JOIN `cloud`.`vm_instance` vm ON backup.vm_id = vm.id SET backup.name = vm.name; +ALTER TABLE `cloud`.`backups` MODIFY COLUMN `name` VARCHAR(255) NOT NULL; +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'description', 'VARCHAR(1024) COMMENT "description for the backup"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.backups', 'backup_interval_type', 'int(5) COMMENT "type of backup, e.g. manual, recurring - hourly, daily, weekly or monthly"'); + +-- Create backup details table +CREATE TABLE IF NOT EXISTS `cloud`.`backup_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `backup_id` bigint unsigned NOT NULL COMMENT 'backup id', + `name` varchar(255) NOT NULL, + `value` TEXT NOT NULL, + `display` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should detail be displayed to the end user', + PRIMARY KEY (`id`), + CONSTRAINT `fk_backup_details__backup_id` FOREIGN KEY `fk_backup_details__backup_id`(`backup_id`) REFERENCES `backups`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Add diskOfferingId, deviceId, minIops and maxIops to backed_volumes in backups table +UPDATE `cloud`.`backups` b +INNER JOIN `cloud`.`vm_instance` vm ON b.vm_id = vm.id +SET b.backed_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +); + +-- Add diskOfferingId, deviceId, minIops and maxIops to backup_volumes in vm_instance table +UPDATE `cloud`.`vm_instance` vm +SET vm.backup_volumes = ( + SELECT CONCAT("[", + GROUP_CONCAT( + CONCAT( + "{\"uuid\":\"", v.uuid, "\",", + "\"type\":\"", v.volume_type, "\",", + "\"size\":", v.`size`, ",", + "\"path\":\"", IFNULL(v.path, 'null'), "\",", + "\"deviceId\":", IFNULL(v.device_id, 'null'), ",", + "\"diskOfferingId\":\"", doff.uuid, "\",", + "\"minIops\":", IFNULL(v.min_iops, 'null'), ",", + "\"maxIops\":", IFNULL(v.max_iops, 'null'), + "}" + ) + SEPARATOR "," + ), + "]") + FROM `cloud`.`volumes` v + LEFT JOIN `cloud`.`disk_offering` doff ON v.disk_offering_id = doff.id + WHERE v.instance_id = vm.id +) +WHERE vm.backup_offering_id IS NOT NULL; + +-- Add column allocated_size to object_store table. Rename column 'used_bytes' to 'used_size' +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.object_store', 'allocated_size', 'bigint unsigned COMMENT "allocated size in bytes"'); +ALTER TABLE `cloud`.`object_store` CHANGE COLUMN `used_bytes` `used_size` BIGINT UNSIGNED COMMENT 'used size in bytes'; +ALTER TABLE `cloud`.`object_store` MODIFY COLUMN `total_size` bigint unsigned COMMENT 'total size in bytes'; +UPDATE `cloud`.`object_store` +JOIN ( + SELECT object_store_id, SUM(quota) AS total_quota + FROM `cloud`.`bucket` + WHERE removed IS NULL + GROUP BY object_store_id +) buckets_quota_sum_view ON `object_store`.id = buckets_quota_sum_view.object_store_id +SET `object_store`.allocated_size = buckets_quota_sum_view.total_quota; + +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.console_session', 'domain_id', 'bigint(20) unsigned NOT NULL'); + +UPDATE `cloud`.`console_session` `cs` +SET `cs`.`domain_id` = ( + SELECT `acc`.`domain_id` + FROM `cloud`.`account` `acc` + WHERE `acc`.`id` = `cs`.`account_id` +); + +-- Re-apply VPC: update default network offering for vpc tier to conserve_mode=1 (#8309) +UPDATE `cloud`.`network_offerings` SET conserve_mode = 1 WHERE name = 'DefaultIsolatedNetworkOfferingForVpcNetworks'; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql new file mode 100644 index 00000000000..9165565f544 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42010to42100cleanup.sql @@ -0,0 +1,20 @@ +-- 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. + +--; +-- Schema upgrade from 4.20.1.0 to 4.21.0.0 +--; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql new file mode 100644 index 00000000000..0546613dfa3 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200-cleanup.sql @@ -0,0 +1,20 @@ +-- 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. + +--; +-- Schema upgrade cleanup from 4.21.0.0 to 4.22.0.0 +--; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql new file mode 100644 index 00000000000..cf3fe2ed772 --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/schema-42100to42200.sql @@ -0,0 +1,23 @@ +-- 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. + +--; +-- Schema upgrade from 4.21.0.0 to 4.22.0.0 +--; + +-- Increase length of scripts_version column to 128 due to md5sum to sha512sum change +CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.domain_router', 'scripts_version', 'scripts_version', 'VARCHAR(128)'); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql index 6092fe8e845..edc164c40cb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.account_view.sql @@ -64,6 +64,8 @@ select `cpucount`.`count` AS `cpuTotal`, `memorylimit`.`max` AS `memoryLimit`, `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, `primary_storage_limit`.`max` AS `primaryStorageLimit`, `primary_storage_count`.`count` AS `primaryStorageTotal`, `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, @@ -156,6 +158,12 @@ from `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id and memorycount.type = 'memory' and memorycount.tag IS NULL left join + `cloud`.`resource_limit` gpulimit ON account.id = gpulimit.account_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON account.id = gpucount.account_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join `cloud`.`resource_limit` primary_storage_limit ON account.id = primary_storage_limit.account_id and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL left join diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql index c34df4f1cbf..46aea863fc5 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.data_center_view.sql @@ -42,6 +42,7 @@ select data_center.type, data_center.removed, data_center.sort_key, + data_center.storage_access_groups, domain.id domain_id, domain.uuid domain_uuid, domain.name domain_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql index 70394e8fd6d..a12e02bcfdb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_router_view.sql @@ -58,6 +58,7 @@ select host.resource_state host_resource_state, vm_template.id template_id, vm_template.uuid template_uuid, + vm_template.arch arch, service_offering.id service_offering_id, service_offering.uuid service_offering_uuid, service_offering.name service_offering_name, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql index c9f7bfc51e4..14fd87536aa 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.domain_view.sql @@ -55,6 +55,8 @@ select `cpucount`.`count` AS `cpuTotal`, `memorylimit`.`max` AS `memoryLimit`, `memorycount`.`count` AS `memoryTotal`, + `gpulimit`.`max` AS `gpuLimit`, + `gpucount`.`count` AS `gpuTotal`, `primary_storage_limit`.`max` AS `primaryStorageLimit`, `primary_storage_count`.`count` AS `primaryStorageTotal`, `secondary_storage_limit`.`max` AS `secondaryStorageLimit`, @@ -130,6 +132,12 @@ from `cloud`.`resource_count` memorycount ON domain.id = memorycount.domain_id and memorycount.type = 'memory' and memorycount.tag IS NULL left join + `cloud`.`resource_limit` gpulimit ON domain.id = gpulimit.domain_id + and gpulimit.type = 'gpu' and gpulimit.tag IS NULL + left join + `cloud`.`resource_count` gpucount ON domain.id = gpucount.domain_id + and gpucount.type = 'gpu' and gpucount.tag IS NULL + left join `cloud`.`resource_limit` primary_storage_limit ON domain.id = primary_storage_limit.domain_id and primary_storage_limit.type = 'primary_storage' and primary_storage_limit.tag IS NULL left join diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql new file mode 100644 index 00000000000..3173274623e --- /dev/null +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.gui_themes_view.sql @@ -0,0 +1,38 @@ +-- 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. + +-- VIEW `cloud`.`gui_themes_view`; + +DROP VIEW IF EXISTS `cloud`.`gui_themes_view`; + +CREATE VIEW `cloud`.`gui_themes_view` AS +SELECT + `cloud`.`gui_themes`.`id` AS `id`, + `cloud`.`gui_themes`.`uuid` AS `uuid`, + `cloud`.`gui_themes`.`name` AS `name`, + `cloud`.`gui_themes`.`description` AS `description`, + `cloud`.`gui_themes`.`css` AS `css`, + `cloud`.`gui_themes`.`json_configuration` AS `json_configuration`, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'commonName' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) common_names, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'domain' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) domains, + (SELECT group_concat(gtd.`value` separator ',') FROM `cloud`.`gui_themes_details` gtd WHERE gtd.`type` = 'account' AND gtd.gui_theme_id = `cloud`.`gui_themes`.`id`) accounts, + `cloud`.`gui_themes`.`recursive_domains` AS `recursive_domains`, + `cloud`.`gui_themes`.`is_public` AS `is_public`, + `cloud`.`gui_themes`.`created` AS `created`, + `cloud`.`gui_themes`.`removed` AS `removed` +FROM `cloud`.`gui_themes` LEFT JOIN `cloud`.`gui_themes_details` ON `cloud`.`gui_themes_details`.`gui_theme_id` = `cloud`.`gui_themes`.`id` +GROUP BY `cloud`.`gui_themes`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql index 6fc8fb80386..d9f4e267159 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.host_view.sql @@ -42,17 +42,21 @@ SELECT host.speed, host.ram, host.arch, + host.storage_access_groups, cluster.id cluster_id, cluster.uuid cluster_uuid, cluster.name cluster_name, cluster.cluster_type, + cluster.storage_access_groups AS cluster_storage_access_groups, data_center.id data_center_id, data_center.uuid data_center_uuid, data_center.name data_center_name, + data_center.storage_access_groups AS zone_storage_access_groups, data_center.networktype data_center_type, host_pod_ref.id pod_id, host_pod_ref.uuid pod_uuid, host_pod_ref.name pod_name, + host_pod_ref.storage_access_groups AS pod_storage_access_groups, GROUP_CONCAT(DISTINCT(host_tags.tag)) AS tag, GROUP_CONCAT(DISTINCT(explicit_host_tags.tag)) AS explicit_tag, GROUP_CONCAT(DISTINCT(implicit_host_tags.tag)) AS implicit_tag, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql index 640b2397a46..368566c32b3 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql @@ -59,8 +59,6 @@ SELECT `network_offerings`.`supports_public_access` AS `supports_public_access`, `network_offerings`.`supports_vm_autoscaling` AS `supports_vm_autoscaling`, `network_offerings`.`for_vpc` AS `for_vpc`, - `network_offerings`.`for_tungsten` AS `for_tungsten`, - `network_offerings`.`for_nsx` AS `for_nsx`, `network_offerings`.`network_mode` AS `network_mode`, `network_offerings`.`service_package_id` AS `service_package_id`, `network_offerings`.`routing_mode` AS `routing_mode`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql index c894429adf8..eb987af3ffb 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql @@ -71,6 +71,20 @@ SELECT `service_offering`.`dynamic_scaling_enabled` AS `dynamic_scaling_enabled`, `service_offering`.`disk_offering_strictness` AS `disk_offering_strictness`, `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + `lease_duration_details`.`value` AS `lease_duration`, + `lease_expiry_action_details`.`value` AS `lease_expiry_action`, + `gpu_card`.`id` AS `gpu_card_id`, + `gpu_card`.`uuid` AS `gpu_card_uuid`, + `gpu_card`.`name` AS `gpu_card_name`, + `vgpu_profile`.`id` AS `vgpu_profile_id`, + `vgpu_profile`.`uuid` AS `vgpu_profile_uuid`, + `vgpu_profile`.`name` AS `vgpu_profile_name`, + `vgpu_profile`.`video_ram` AS `vgpu_profile_video_ram`, + `vgpu_profile`.`max_heads` AS `vgpu_profile_max_heads`, + `vgpu_profile`.`max_resolution_x` AS `vgpu_profile_max_resolution_x`, + `vgpu_profile`.`max_resolution_y` AS `vgpu_profile_max_resolution_y`, + `service_offering`.`gpu_count` AS `gpu_count`, + `service_offering`.`gpu_display` AS `gpu_display`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, @@ -87,6 +101,10 @@ FROM INNER JOIN `cloud`.`disk_offering` ON service_offering.disk_offering_id = disk_offering.id LEFT JOIN + `cloud`.`vgpu_profile` ON service_offering.vgpu_profile_id = vgpu_profile.id + LEFT JOIN + `cloud`.`gpu_card` ON vgpu_profile.card_id = gpu_card.id + LEFT JOIN `cloud`.`service_offering_details` AS `domain_details` ON `domain_details`.`service_offering_id` = `service_offering`.`id` AND `domain_details`.`name`='domainid' LEFT JOIN `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) @@ -109,5 +127,11 @@ FROM LEFT JOIN `cloud`.`service_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`service_offering_id` = `service_offering`.`id` AND `vsphere_storage_policy`.`name` = 'storagepolicy' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_duration_details` ON `lease_duration_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_duration_details`.`name` = 'leaseduration' + LEFT JOIN + `cloud`.`service_offering_details` AS `lease_expiry_action_details` ON `lease_expiry_action_details`.`service_offering_id` = `service_offering`.`id` + AND `lease_expiry_action_details`.`name` = 'leaseexpiryaction' GROUP BY `service_offering`.`id`; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql index 5d7585baa3b..641017bdd5b 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.storage_pool_view.sql @@ -51,6 +51,7 @@ SELECT `host_pod_ref`.`name` AS `pod_name`, `storage_pool_tags`.`tag` AS `tag`, `storage_pool_tags`.`is_tag_a_rule` AS `is_tag_a_rule`, + `storage_pool_and_access_group_map`.`storage_access_group` AS `storage_access_group`, `op_host_capacity`.`used_capacity` AS `disk_used_capacity`, `op_host_capacity`.`reserved_capacity` AS `disk_reserved_capacity`, `async_job`.`id` AS `job_id`, @@ -58,13 +59,16 @@ SELECT `async_job`.`job_status` AS `job_status`, `async_job`.`account_id` AS `job_account_id` FROM - ((((((`cloud`.`storage_pool` - LEFT JOIN `cloud`.`cluster` ON ((`storage_pool`.`cluster_id` = `cluster`.`id`))) - LEFT JOIN `cloud`.`data_center` ON ((`storage_pool`.`data_center_id` = `data_center`.`id`))) - LEFT JOIN `cloud`.`host_pod_ref` ON ((`storage_pool`.`pod_id` = `host_pod_ref`.`id`))) - LEFT JOIN `cloud`.`storage_pool_tags` ON (((`storage_pool_tags`.`pool_id` = `storage_pool`.`id`)))) - LEFT JOIN `cloud`.`op_host_capacity` ON (((`storage_pool`.`id` = `op_host_capacity`.`host_id`) - AND (`op_host_capacity`.`capacity_type` IN (3 , 9))))) - LEFT JOIN `cloud`.`async_job` ON (((`async_job`.`instance_id` = `storage_pool`.`id`) - AND (`async_job`.`instance_type` = 'StoragePool') - AND (`async_job`.`job_status` = 0)))); + `cloud`.`storage_pool` + LEFT JOIN `cloud`.`cluster` ON `storage_pool`.`cluster_id` = `cluster`.`id` + LEFT JOIN `cloud`.`data_center` ON `storage_pool`.`data_center_id` = `data_center`.`id` + LEFT JOIN `cloud`.`host_pod_ref` ON `storage_pool`.`pod_id` = `host_pod_ref`.`id` + LEFT JOIN `cloud`.`storage_pool_tags` ON `storage_pool_tags`.`pool_id` = `storage_pool`.`id` + LEFT JOIN `cloud`.`storage_pool_and_access_group_map` ON `storage_pool_and_access_group_map`.`pool_id` = `storage_pool`.`id` + LEFT JOIN `cloud`.`op_host_capacity` + ON `storage_pool`.`id` = `op_host_capacity`.`host_id` + AND `op_host_capacity`.`capacity_type` IN (3, 9) + LEFT JOIN `cloud`.`async_job` + ON `async_job`.`instance_id` = `storage_pool`.`id` + AND `async_job`.`instance_type` = 'StoragePool' + AND `async_job`.`job_status` = 0; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql index 339e43860d8..76a8be16bda 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.template_view.sql @@ -41,6 +41,7 @@ SELECT `vm_template`.`guest_os_id` AS `guest_os_id`, `guest_os`.`uuid` AS `guest_os_uuid`, `guest_os`.`display_name` AS `guest_os_name`, + `guest_os`.`category_id` AS `guest_os_category_id`, `vm_template`.`bootable` AS `bootable`, `vm_template`.`prepopulate` AS `prepopulate`, `vm_template`.`cross_zones` AS `cross_zones`, @@ -100,11 +101,15 @@ SELECT IFNULL(`data_center`.`id`, 0)) AS `temp_zone_pair`, `vm_template`.`direct_download` AS `direct_download`, `vm_template`.`deploy_as_is` AS `deploy_as_is`, + `vm_template`.`for_cks` AS `for_cks`, `user_data`.`id` AS `user_data_id`, `user_data`.`uuid` AS `user_data_uuid`, `user_data`.`name` AS `user_data_name`, `user_data`.`params` AS `user_data_params`, - `vm_template`.`user_data_link_policy` AS `user_data_policy` + `vm_template`.`user_data_link_policy` AS `user_data_policy`, + `extension`.`id` AS `extension_id`, + `extension`.`uuid` AS `extension_uuid`, + `extension`.`name` AS `extension_name` FROM (((((((((((((`vm_template` JOIN `guest_os` ON ((`guest_os`.`id` = `vm_template`.`guest_os_id`))) @@ -127,6 +132,7 @@ FROM OR (`template_zone_ref`.`zone_id` = `data_center`.`id`)))) LEFT JOIN `launch_permission` ON ((`launch_permission`.`template_id` = `vm_template`.`id`))) LEFT JOIN `user_data` ON ((`user_data`.`id` = `vm_template`.`user_data_id`)) + LEFT JOIN `extension` ON ((`extension`.`id` = `vm_template`.`extension_id`)) LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_template`.`id`) AND ((`resource_tags`.`resource_type` = 'Template') OR (`resource_tags`.`resource_type` = 'ISO'))))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql index 97cb7b735cf..94bc8640fd5 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -83,6 +83,7 @@ SELECT `iso`.`uuid` AS `iso_uuid`, `iso`.`name` AS `iso_name`, `iso`.`display_text` AS `iso_display_text`, + `vm_template`.`arch` AS `arch`, `service_offering`.`id` AS `service_offering_id`, `service_offering`.`uuid` AS `service_offering_uuid`, `disk_offering`.`uuid` AS `disk_offering_uuid`, @@ -102,6 +103,17 @@ SELECT `backup_offering`.`uuid` AS `backup_offering_uuid`, `backup_offering`.`id` AS `backup_offering_id`, `service_offering`.`name` AS `service_offering_name`, + `service_offering`.`vgpu_profile_id` AS `vgpu_profile_id`, + `vgpu_profile`.`uuid` AS `vgpu_profile_uuid`, + `vgpu_profile`.`name` AS `vgpu_profile_name`, + `vgpu_profile`.`video_ram` AS `vgpu_profile_video_ram`, + `vgpu_profile`.`max_heads` AS `vgpu_profile_max_heads`, + `vgpu_profile`.`max_resolution_x` AS `vgpu_profile_max_resolution_x`, + `vgpu_profile`.`max_resolution_y` AS `vgpu_profile_max_resolution_y`, + `gpu_card`.`id` AS `gpu_card_id`, + `gpu_card`.`uuid` AS `gpu_card_uuid`, + `gpu_card`.`name` AS `gpu_card_name`, + `service_offering`.`gpu_count` AS `gpu_count`, `disk_offering`.`name` AS `disk_offering_name`, `backup_offering`.`name` AS `backup_offering_name`, `storage_pool`.`id` AS `pool_id`, @@ -168,9 +180,12 @@ SELECT `user_data`.`uuid` AS `user_data_uuid`, `user_data`.`name` AS `user_data_name`, `user_vm`.`user_data_details` AS `user_data_details`, - `vm_template`.`user_data_link_policy` AS `user_data_policy` + `vm_template`.`user_data_link_policy` AS `user_data_policy`, + `lease_expiry_date`.`value` AS `lease_expiry_date`, + `lease_expiry_action`.`value` AS `lease_expiry_action`, + `lease_action_execution`.`value` AS `lease_action_execution` FROM - (((((((((((((((((((((((((((((((((((`user_vm` + (((((((((((((((((((((((((((((((((((((`user_vm` JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`) AND ISNULL(`vm_instance`.`removed`)))) JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`))) @@ -188,6 +203,8 @@ FROM LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`))) LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`volumes`.`disk_offering_id` = `svc_disk_offering`.`id`))) LEFT JOIN `disk_offering` ON ((`volumes`.`disk_offering_id` = `disk_offering`.`id`))) + LEFT JOIN `vgpu_profile` ON ((`service_offering`.`vgpu_profile_id` = `vgpu_profile`.`id`))) + LEFT JOIN `gpu_card` ON ((`vgpu_profile`.`card_id` = `gpu_card`.`id`))) LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`))) LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`))) LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`))) @@ -199,7 +216,7 @@ FROM LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`) AND ISNULL(`vpc`.`removed`)))) LEFT JOIN `user_ip_address` FORCE INDEX(`fk_user_ip_address__vm_id`) ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`))) - LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `vm_instance_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`) AND (`ssh_details`.`name` = 'SSH.KeyPairNames')))) LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`) AND (`resource_tags`.`resource_type` = 'UserVm')))) @@ -210,9 +227,15 @@ FROM LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`))) LEFT JOIN `autoscale_vmgroup_vm_map` ON ((`autoscale_vmgroup_vm_map`.`instance_id` = `vm_instance`.`id`))) LEFT JOIN `autoscale_vmgroups` ON ((`autoscale_vmgroup_vm_map`.`vmgroup_id` = `autoscale_vmgroups`.`id`))) - LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `vm_instance_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`) AND (`custom_cpu`.`name` = 'CpuNumber')))) - LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`) + LEFT JOIN `vm_instance_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`) AND (`custom_speed`.`name` = 'CpuSpeed')))) - LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`) - AND (`custom_ram_size`.`name` = 'memory')))); + LEFT JOIN `vm_instance_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`) + AND (`custom_ram_size`.`name` = 'memory'))) + LEFT JOIN `vm_instance_details` `lease_expiry_date` ON ((`lease_expiry_date`.`vm_id` = `vm_instance`.`id`) + AND (`lease_expiry_date`.`name` = 'leaseexpirydate')) + LEFT JOIN `vm_instance_details` `lease_action_execution` ON ((`lease_action_execution`.`vm_id` = `vm_instance`.`id`) + AND (`lease_action_execution`.`name` = 'leaseactionexecution')) + LEFT JOIN `vm_instance_details` `lease_expiry_action` ON (((`lease_expiry_action`.`vm_id` = `vm_instance`.`id`) + AND (`lease_expiry_action`.`name` = 'leaseexpiryaction')))); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql index c74d50590de..751d8f91a25 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql @@ -28,7 +28,6 @@ select `vpc_offerings`.`display_text` AS `display_text`, `vpc_offerings`.`state` AS `state`, `vpc_offerings`.`default` AS `default`, - `vpc_offerings`.`for_nsx` AS `for_nsx`, `vpc_offerings`.`network_mode` AS `network_mode`, `vpc_offerings`.`created` AS `created`, `vpc_offerings`.`removed` AS `removed`, diff --git a/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java index 76c1092546a..f9528d5d57f 100644 --- a/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java @@ -16,31 +16,45 @@ // under the License. package com.cloud.capacity.dao; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - +import com.cloud.capacity.CapacityVO; +import com.cloud.host.Host; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import org.junit.After; import org.junit.Before; 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.capacity.CapacityVO; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class CapacityDaoImplTest { @@ -48,8 +62,21 @@ public class CapacityDaoImplTest { @InjectMocks CapacityDaoImpl capacityDao = new CapacityDaoImpl(); + @Mock + private CapacityVO mockEntity; + + @Mock + private TransactionLegacy txn; + @Mock + private PreparedStatement pstmt; + @Mock + private ResultSet resultSet; + private MockedStatic mockedTransactionLegacy; + private SearchBuilder searchBuilder; private SearchCriteria searchCriteria; + private List capacityTypes; + private List expectedCapacities; @Before public void setUp() { @@ -59,6 +86,27 @@ public class CapacityDaoImplTest { searchCriteria = mock(SearchCriteria.class); doReturn(searchBuilder).when(capacityDao).createSearchBuilder(); when(searchBuilder.create()).thenReturn(searchCriteria); + + mockedTransactionLegacy = Mockito.mockStatic(TransactionLegacy.class); + mockedTransactionLegacy.when(TransactionLegacy::currentTxn).thenReturn(txn); + + // Setup common test data + capacityTypes = Arrays.asList((short) 1, (short) 2, (short) 3); + expectedCapacities = Arrays.asList(mock(CapacityVO.class), mock(CapacityVO.class)); + doReturn(expectedCapacities).when(capacityDao).listBy(searchCriteria); + } + + private CapacityVO createMockCapacityVO(Long id) { + CapacityVO capacity = mock(CapacityVO.class); + when(capacity.getId()).thenReturn(id); + return capacity; + } + + @After + public void tearDown() { + if (mockedTransactionLegacy != null) { + mockedTransactionLegacy.close(); + } } @Test @@ -96,4 +144,410 @@ public class CapacityDaoImplTest { verify(capacityDao).listBy(searchCriteria); assertTrue(result.isEmpty()); } + + @Test + public void testListClustersCrossingThresholdEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + List result = capacityDao.listClustersCrossingThreshold((short)1, 1L, "cpu.threshold", 5000L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testFindCapacityByZoneAndHostTagNoResults() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Ternary result = capacityDao.findCapacityByZoneAndHostTag(1L, "host-tag"); + assertNotNull(result); + assertEquals(Long.valueOf(0L), result.first()); + assertEquals(Long.valueOf(0L), result.second()); + assertEquals(Long.valueOf(0L), result.third()); + } + @Test + public void testFindByHostIdType() { + CapacityVO capacity = new CapacityVO(); + capacity.setHostId(1L); + capacity.setCapacityType((short) 1); + + doReturn(capacity).when(capacityDao).findOneBy(any()); + + CapacityVO found = capacityDao.findByHostIdType(1L, (short) 1); + assertNotNull(found); + assertEquals(Long.valueOf(1L), found.getHostOrPoolId()); + } + + @Test + public void testUpdateAllocatedAddition() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + doNothing().when(txn).start(); + when(txn.commit()).thenReturn(true); + + capacityDao.updateAllocated(1L, 1000L, (short)1, true); + + verify(txn, times(1)).start(); + verify(txn, times(1)).commit(); + verify(pstmt, times(1)).executeUpdate(); + } + + @Test + public void testUpdateAllocatedSubtraction() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + doNothing().when(txn).start(); + when(txn.commit()).thenReturn(true); + + capacityDao.updateAllocated(1L, 500L, (short)1, false); + + verify(txn, times(1)).start(); + verify(txn, times(1)).commit(); + verify(pstmt, times(1)).executeUpdate(); + } + + @Test + public void testFindFilteredCapacityByEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + List result = capacityDao.findFilteredCapacityBy(null, null, null, null, Collections.emptyList(), Collections.emptyList()); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListClustersInZoneOrPodByHostCapacitiesEmpty() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List resultZone = capacityDao.listClustersInZoneOrPodByHostCapacities(1L, 123L, 2, 2048L, true); + assertNotNull(resultZone); + assertTrue(resultZone.isEmpty()); + + List resultPod = capacityDao.listClustersInZoneOrPodByHostCapacities(1L, 123L, 2, 2048L, false); + assertNotNull(resultPod); + assertTrue(resultPod.isEmpty()); + } + + + @Test + public void testListHostsWithEnoughCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listHostsWithEnoughCapacity(1, 100L, 200L, Host.Type.Routing.toString()); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + + @Test + public void testOrderClustersByAggregateCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(1L, 1L, (short) 1, true); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + assertTrue(result.second().isEmpty()); + } + + + @Test + public void testOrderPodsByAggregateCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderPodsByAggregateCapacity(1L, (short) 1); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + assertTrue(result.second().isEmpty()); + } + + + @Test + public void testUpdateCapacityState() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeUpdate()).thenReturn(1); + + capacityDao.updateCapacityState(1L, 1L, 1L, 1L, "Enabled", new short[]{1}); + + verify(pstmt, times(1)).executeUpdate(); + } + + + @Test + public void testFindClusterConsumption() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getFloat(1)).thenReturn(0.5f); + + float result = capacityDao.findClusterConsumption(1L, (short) 1, 1000L); + assertEquals(0.5f, result, 0.0f); + } + + @Test + public void testListPodsByHostCapacitiesEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listPodsByHostCapacities(1L, 2, 1024L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testOrderHostsByFreeCapacityEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + Pair, Map> result = capacityDao.orderHostsByFreeCapacity(1L, 1L, (short) 0); + assertNotNull(result); + assertTrue(result.first().isEmpty()); + } + + @Test + public void testFindByClusterPodZoneEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.findByClusterPodZone(1L, 1L, 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListCapacitiesGroupedByLevelAndTypeEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.listCapacitiesGroupedByLevelAndType(0, 1L, + 1L, 1L, 0, Collections.emptyList(), Collections.emptyList(), 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testFindCapacityByEmptyResult() throws Exception { + when(txn.prepareAutoCloseStatement(anyString())).thenReturn(pstmt); + when(pstmt.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(false); + + List result = capacityDao.findCapacityBy(1, 1L, 1L, 1L); + assertNotNull(result); + assertTrue(result.isEmpty()); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + Long clusterId = 200L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("clusterId", mockEntity.getClusterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("clusterId", clusterId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + verify(capacityDao).listBy(searchCriteria); + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithNullZoneId() { + // Given + Long clusterId = 200L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(null, clusterId, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, Mockito.times(0)).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("clusterId", clusterId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithNullClusterId() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria, never()).setParameters(eq("clusterId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListHostCapacityByCapacityTypes_WithEmptyCapacityTypes() { + // Given + Long zoneId = 100L; + Long clusterId = 200L; + List emptyCapacityTypes = Collections.emptyList(); + + // When + List result = capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, emptyCapacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityTypes", emptyCapacityTypes.toArray()); + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListPodCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listPodCapacityByCapacityTypes(zoneId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListPodCapacityByCapacityTypes_WithNullZoneId() { + // When + List result = capacityDao.listPodCapacityByCapacityTypes(null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithAllParameters() { + // Given + Long zoneId = 100L; + Long podId = 300L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(zoneId, podId, capacityTypes); + + // Then + verify(searchBuilder).and("zoneId", mockEntity.getDataCenterId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("podId", mockEntity.getPodId(), SearchCriteria.Op.EQ); + verify(searchBuilder).and("capacityTypes", mockEntity.getCapacityType(), SearchCriteria.Op.IN); + verify(searchBuilder).and("capacityState", mockEntity.getCapacityState(), SearchCriteria.Op.EQ); + + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria).setParameters("podId", podId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithNullZoneId() { + // Given + Long podId = 300L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(null, podId, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria).setParameters("podId", podId); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithNullPodId() { + // Given + Long zoneId = 100L; + + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(zoneId, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria).setParameters("zoneId", zoneId); + verify(searchCriteria, never()).setParameters(eq("podId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testListClusterCapacityByCapacityTypes_WithBothIdsNull() { + // When + List result = capacityDao.listClusterCapacityByCapacityTypes(null, null, capacityTypes); + + // Then + verify(searchCriteria).setParameters("capacityState", "Enabled"); + verify(searchCriteria, never()).setParameters(eq("zoneId"), any()); + verify(searchCriteria, never()).setParameters(eq("podId"), any()); + verify(searchCriteria).setParameters("capacityTypes", capacityTypes.toArray()); + + assertEquals("Should return expected capacities", expectedCapacities, result); + } + + @Test + public void testAllMethods_VerifySearchBuilderSetup() { + // Test that all methods properly set up the search builder + Long zoneId = 100L; + Long clusterId = 200L; + Long podId = 300L; + + // Test host capacity method + capacityDao.listHostCapacityByCapacityTypes(zoneId, clusterId, capacityTypes); + + // Test pod capacity method + capacityDao.listPodCapacityByCapacityTypes(zoneId, capacityTypes); + + // Test cluster capacity method + capacityDao.listClusterCapacityByCapacityTypes(zoneId, podId, capacityTypes); + + // Verify createSearchBuilder was called 3 times + verify(capacityDao, times(3)).createSearchBuilder(); + + // Verify done() was called 3 times + verify(searchBuilder, times(3)).done(); + + // Verify listBy was called 3 times + verify(capacityDao, times(3)).listBy(searchCriteria); + } } diff --git a/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java b/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java index a513809be05..c1a558a72b3 100644 --- a/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/dc/dao/ClusterDaoImplTest.java @@ -17,6 +17,7 @@ package com.cloud.dc.dao; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; @@ -36,9 +37,13 @@ import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.cpu.CPU; import com.cloud.dc.ClusterVO; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.utils.Pair; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; @RunWith(MockitoJUnitRunner.class) public class ClusterDaoImplTest { @@ -75,4 +80,39 @@ public class ClusterDaoImplTest { verify(clusterDao).customSearch(genericSearchBuilder.create(), null); assertTrue(result.isEmpty()); } + + @Test + public void listDistinctHypervisorsArchAcrossClusters_WithZone() { + Long zoneId = 123L; + ClusterVO cluster1 = mock(ClusterVO.class); + when(cluster1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + when(cluster1.getArch()).thenReturn(CPU.CPUArch.amd64); + ClusterVO cluster2 = mock(ClusterVO.class); + when(cluster2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(cluster2.getArch()).thenReturn(CPU.CPUArch.arm64); + List dummyHosts = Arrays.asList(cluster1, cluster2); + doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull()); + List> result = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first()); + assertEquals(CPU.CPUArch.arm64, result.get(1).second()); + } + + @Test + public void listDistinctHypervisorsArchAcrossClusters_WithoutZone() { + Long zoneId = null; + ClusterVO cluster = mock(ClusterVO.class); + when(cluster.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware); + when(cluster.getArch()).thenReturn(CPU.CPUArch.amd64); + List dummyHosts = Collections.singletonList(cluster); + doReturn(dummyHosts).when(clusterDao).search(any(SearchCriteria.class), isNull()); + List> result = clusterDao.listDistinctHypervisorsAndArchExcludingExternalType(zoneId); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + } } diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java new file mode 100644 index 00000000000..e0a283add99 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuCardDaoImplTest.java @@ -0,0 +1,57 @@ +/* + * 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.gpu.dao; + +import com.cloud.gpu.GpuCardVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class GpuCardDaoImplTest { + + @Spy + @InjectMocks + GpuCardDaoImpl gpuCardDaoImpl = new GpuCardDaoImpl(); + + @Test + public void findByVendorIdAndDeviceId() { + doReturn(mock(GpuCardVO.class)).when(gpuCardDaoImpl).findOneBy(any(SearchCriteria.class)); + + GpuCardVO gpuCard = gpuCardDaoImpl.findByVendorIdAndDeviceId("0d1a", "1a3b"); + Assert.assertNotNull("Expected non-null gpu card", gpuCard); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuCardDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_card.vendor_id = ? AND gpu_card.device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java new file mode 100644 index 00000000000..1780fbd3df3 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/GpuDeviceDaoImplTest.java @@ -0,0 +1,277 @@ +/* + * 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.gpu.dao; + +import com.cloud.gpu.GpuDeviceVO; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.SearchCriteria; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class GpuDeviceDaoImplTest { + + @Spy + @InjectMocks + GpuDeviceDaoImpl gpuDeviceDao = new GpuDeviceDaoImpl(); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void listByIds_emptyList() { + List devices = gpuDeviceDao.listByIds(null); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + devices = gpuDeviceDao.listByIds(Collections.emptyList()); + Assert.assertTrue("Expected empty list", devices.isEmpty()); + } + + @Test + public void listByIds() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByIds(List.of(1L, 2L, 3L)); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + SearchCriteria sc = scCaptor.getValue(); + Assert.assertEquals("Expected correct where clause", "gpu_device.id IN (?,?,?)", sc.getWhereClause().trim()); + } + + @Test + public void findByHostIdAndBusAddress() { + doReturn(mock(GpuDeviceVO.class)).when(gpuDeviceDao).findOneBy(any(SearchCriteria.class)); + + GpuDeviceVO device = gpuDeviceDao.findByHostIdAndBusAddress(1L, "0000:00:1f.6"); + + Assert.assertNotNull("Expected non-null device", device); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.bus_address = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByHostId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByVmId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByVmId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + + Assert.assertEquals("Expected correct where clause", "gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isVgpuProfileInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isVgpuProfileInUse(1L); + + Assert.assertTrue("Expected vGPU profile to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.vgpu_profile_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void isGpuCardInUse() { + doReturn(1).when(gpuDeviceDao).getCount(any(SearchCriteria.class)); + + boolean vgpuProfileInUse = gpuDeviceDao.isGpuCardInUse(1L); + + Assert.assertTrue("Expected GPU Card to be in use", vgpuProfileInUse); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).getCount(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.card_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void listByHostAndVm() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listByHostAndVm(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.host_id = ? AND gpu_device.vm_id = ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void listDevicesForAllocation() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).search(any(SearchCriteria.class), any()); + + List devices = gpuDeviceDao.listDevicesForAllocation(1L, 2L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + ArgumentCaptor filterCaptor = ArgumentCaptor.forClass(Filter.class); + verify(gpuDeviceDao).search(scCaptor.capture(), filterCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "gpu_device.host_id = ? AND gpu_device.vgpu_profile_id=? AND gpu_device.state = ? AND gpu_device" + + ".managed_state = ? AND gpu_device.type != ?", + scCaptor.getValue().getWhereClause().trim()); + Assert.assertNull("Expected no filter", filterCaptor.getValue()); + } + + @Test + public void searchAndCountGpuDevices() { + } + + @Test + public void getDistinctGpuCardIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctGpuCardIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getCardId()).thenReturn(1L); + when(device2.getCardId()).thenReturn(2L); + when(device3.getCardId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctGpuCardIds(); + + Assert.assertEquals("Expected 2 card IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected card ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected card ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void getDistinctVgpuProfileIds_no_devices() { + doReturn(null).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertTrue("Expected empty list", cardIds.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void getDistinctVgpuProfileIds() { + GpuDeviceVO device1 = mock(GpuDeviceVO.class); + GpuDeviceVO device2 = mock(GpuDeviceVO.class); + GpuDeviceVO device3 = mock(GpuDeviceVO.class); + when(device1.getVgpuProfileId()).thenReturn(1L); + when(device2.getVgpuProfileId()).thenReturn(2L); + when(device3.getVgpuProfileId()).thenReturn(1L); + + doReturn(List.of(device1, device2, device3)).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List cardIds = gpuDeviceDao.getDistinctVgpuProfileIds(); + + Assert.assertEquals("Expected 2 VgpuProfile IDs", 2, cardIds.size()); + + Assert.assertTrue("Expected VgpuProfile ID 1 in list", cardIds.contains(1L)); + Assert.assertTrue("Expected VgpuProfile ID 2 in list", cardIds.contains(2L)); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "", scCaptor.getValue().getWhereClause().trim()); + } + + + @Test + public void listByParentGpuDeviceId() { + doReturn(List.of(mock(GpuDeviceVO.class))).when(gpuDeviceDao).listBy(any(SearchCriteria.class)); + + List devices = gpuDeviceDao.listByParentGpuDeviceId(1L); + + Assert.assertFalse("Expected non empty list", devices.isEmpty()); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(gpuDeviceDao).listBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "gpu_device.parent_gpu_device_id = ?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java new file mode 100644 index 00000000000..cd7199d020f --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/gpu/dao/VgpuProfileDaoImplTest.java @@ -0,0 +1,70 @@ +/* + * 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.gpu.dao; + +import com.cloud.gpu.VgpuProfileVO; +import com.cloud.utils.db.SearchCriteria; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(MockitoJUnitRunner.class) +public class VgpuProfileDaoImplTest { + + @Spy + @InjectMocks + VgpuProfileDaoImpl vgpuProfileDaoImpl = new VgpuProfileDaoImpl(); + + @Test + public void findByNameAndCardId() { + doReturn(mock(VgpuProfileVO.class)).when(vgpuProfileDaoImpl).findOneBy(any(SearchCriteria.class)); + + VgpuProfileVO vgpuProfile = vgpuProfileDaoImpl.findByNameAndCardId("test-profile", 1L); + Assert.assertNotNull("Expected non-null vgpu profile", vgpuProfile); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).findOneBy(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", + "vgpu_profile.name = ? AND vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } + + @Test + public void removeByCardId() { + doReturn(1).when(vgpuProfileDaoImpl).remove(any(SearchCriteria.class)); + + int removed = vgpuProfileDaoImpl.removeByCardId(123L); + Assert.assertEquals("Expected one vgpu profile removed", 1, removed); + + ArgumentCaptor scCaptor = ArgumentCaptor.forClass(SearchCriteria.class); + verify(vgpuProfileDaoImpl).remove(scCaptor.capture()); + Assert.assertEquals("Expected correct where clause", "vgpu_profile.card_id=?", + scCaptor.getValue().getWhereClause().trim()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java b/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java index 81163321c6b..c76eedb01e9 100644 --- a/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/host/dao/HostDaoImplTest.java @@ -16,7 +16,18 @@ // under the License. package com.cloud.host.dao; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Test; @@ -26,6 +37,7 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.cpu.CPU; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -52,10 +64,10 @@ public class HostDaoImplTest { public void testCountUpAndEnabledHostsInZone() { long testZoneId = 100L; hostDao.HostTypeCountSearch = mockSearchBuilder; - Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); - Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), Mockito.any()); + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + Mockito.doNothing().when(mockSearchCriteria).setParameters(Mockito.anyString(), any()); int expected = 5; - Mockito.doReturn(expected).when(hostDao).getCount(mockSearchCriteria); + doReturn(expected).when(hostDao).getCount(mockSearchCriteria); Integer count = hostDao.countUpAndEnabledHostsInZone(testZoneId); Assert.assertSame(expected, count); Mockito.verify(mockSearchCriteria).setParameters("type", Host.Type.Routing); @@ -70,16 +82,16 @@ public class HostDaoImplTest { GenericDaoBase.SumCount mockSumCount = new GenericDaoBase.SumCount(); mockSumCount.count = 10; mockSumCount.sum = 20; - HostVO host = Mockito.mock(HostVO.class); - GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); - Mockito.when(sb.entity()).thenReturn(host); - Mockito.doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class); - SearchCriteria sc = Mockito.mock(SearchCriteria.class); - Mockito.when(sb.create()).thenReturn(sc); - Mockito.doReturn(List.of(mockSumCount)).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + doReturn(sb).when(hostDao).createSearchBuilder(GenericDaoBase.SumCount.class); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(List.of(mockSumCount)).when(hostDao).customSearch(any(SearchCriteria.class), any()); Pair result = hostDao.countAllHostsAndCPUSocketsByType(type); - Assert.assertEquals(10, result.first().intValue()); - Assert.assertEquals(20, result.second().intValue()); + assertEquals(10, result.first().intValue()); + assertEquals(20, result.second().intValue()); Mockito.verify(sc).setParameters("type", type); } @@ -87,13 +99,13 @@ public class HostDaoImplTest { public void testIsHostUp() { long testHostId = 101L; List statuses = List.of(Status.Up); - HostVO host = Mockito.mock(HostVO.class); - GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); - Mockito.when(sb.entity()).thenReturn(host); - SearchCriteria sc = Mockito.mock(SearchCriteria.class); - Mockito.when(sb.create()).thenReturn(sc); - Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Status.class); - Mockito.doReturn(statuses).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(Status.class); + doReturn(statuses).when(hostDao).customSearch(any(SearchCriteria.class), any()); boolean result = hostDao.isHostUp(testHostId); Assert.assertTrue("Host should be up", result); Mockito.verify(sc).setParameters("id", testHostId); @@ -104,23 +116,25 @@ public class HostDaoImplTest { public void testFindHostIdsByZoneClusterResourceStateTypeAndHypervisorType() { Long zoneId = 1L; Long clusterId = 2L; + Long msId = 1L; List resourceStates = List.of(ResourceState.Enabled); List types = List.of(Host.Type.Routing); List hypervisorTypes = List.of(Hypervisor.HypervisorType.KVM); List mockResults = List.of(1001L, 1002L); // Mocked result - HostVO host = Mockito.mock(HostVO.class); - GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); - Mockito.when(sb.entity()).thenReturn(host); - SearchCriteria sc = Mockito.mock(SearchCriteria.class); - Mockito.when(sb.create()).thenReturn(sc); - Mockito.when(sb.and()).thenReturn(sb); - Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class); - Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + when(sb.and()).thenReturn(sb); + doReturn(sb).when(hostDao).createSearchBuilder(Long.class); + doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any()); List hostIds = hostDao.findHostIdsByZoneClusterResourceStateTypeAndHypervisorType( - zoneId, clusterId, resourceStates, types, hypervisorTypes); - Assert.assertEquals(mockResults, hostIds); + zoneId, clusterId, msId, resourceStates, types, hypervisorTypes); + assertEquals(mockResults, hostIds); Mockito.verify(sc).setParameters("zoneId", zoneId); Mockito.verify(sc).setParameters("clusterId", clusterId); + Mockito.verify(sc).setParameters("msId", msId); Mockito.verify(sc).setParameters("resourceState", resourceStates.toArray()); Mockito.verify(sc).setParameters("type", types.toArray()); Mockito.verify(sc).setParameters("hypervisorTypes", hypervisorTypes.toArray()); @@ -130,15 +144,16 @@ public class HostDaoImplTest { public void testListDistinctHypervisorTypes() { Long zoneId = 1L; List mockResults = List.of(Hypervisor.HypervisorType.KVM, Hypervisor.HypervisorType.XenServer); - HostVO host = Mockito.mock(HostVO.class); - GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); - Mockito.when(sb.entity()).thenReturn(host); - SearchCriteria sc = Mockito.mock(SearchCriteria.class); - Mockito.when(sb.create()).thenReturn(sc); - Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Hypervisor.HypervisorType.class); - Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(String.class); + doReturn(mockResults.stream().map(h -> h.name()).collect(Collectors.toList())).when(hostDao) + .customSearch(any(SearchCriteria.class), any()); List hypervisorTypes = hostDao.listDistinctHypervisorTypes(zoneId); - Assert.assertEquals(mockResults, hypervisorTypes); + assertEquals(mockResults, hypervisorTypes); Mockito.verify(sc).setParameters("zoneId", zoneId); Mockito.verify(sc).setParameters("type", Host.Type.Routing); } @@ -146,12 +161,12 @@ public class HostDaoImplTest { @Test public void testListByIds() { List ids = List.of(101L, 102L); - List mockResults = List.of(Mockito.mock(HostVO.class), Mockito.mock(HostVO.class)); + List mockResults = List.of(mock(HostVO.class), mock(HostVO.class)); hostDao.IdsSearch = mockSearchBuilder; - Mockito.when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); - Mockito.doReturn(mockResults).when(hostDao).search(Mockito.any(SearchCriteria.class), Mockito.any()); + when(mockSearchBuilder.create()).thenReturn(mockSearchCriteria); + doReturn(mockResults).when(hostDao).search(any(SearchCriteria.class), any()); List hosts = hostDao.listByIds(ids); - Assert.assertEquals(mockResults, hosts); + assertEquals(mockResults, hosts); Mockito.verify(mockSearchCriteria).setParameters("id", ids.toArray()); Mockito.verify(hostDao).search(mockSearchCriteria, null); } @@ -164,15 +179,15 @@ public class HostDaoImplTest { Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; Long zoneId = 1L, podId = 2L, clusterId = 3L; List mockResults = List.of(1001L, 1002L); - HostVO host = Mockito.mock(HostVO.class); - GenericSearchBuilder sb = Mockito.mock(GenericSearchBuilder.class); - Mockito.when(sb.entity()).thenReturn(host); - SearchCriteria sc = Mockito.mock(SearchCriteria.class); - Mockito.when(sb.create()).thenReturn(sc); - Mockito.doReturn(sb).when(hostDao).createSearchBuilder(Long.class); - Mockito.doReturn(mockResults).when(hostDao).customSearch(Mockito.any(SearchCriteria.class), Mockito.any()); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(Long.class); + doReturn(mockResults).when(hostDao).customSearch(any(SearchCriteria.class), any()); List hostIds = hostDao.listIdsBy(type, status, resourceState, hypervisorType, zoneId, podId, clusterId); - Assert.assertEquals(mockResults, hostIds); + assertEquals(mockResults, hostIds); Mockito.verify(sc).setParameters("type", type); Mockito.verify(sc).setParameters("status", status); Mockito.verify(sc).setParameters("resourceState", resourceState); @@ -181,4 +196,57 @@ public class HostDaoImplTest { Mockito.verify(sc).setParameters("podId", podId); Mockito.verify(sc).setParameters("clusterId", clusterId); } + + @Test + public void testListDistinctHypervisorArchTypes_WithZone() { + Long zoneId = 123L; + HostVO host1 = mock(HostVO.class); + when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + when(host1.getArch()).thenReturn(CPU.CPUArch.amd64); + HostVO host2 = mock(HostVO.class); + when(host2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(host2.getArch()).thenReturn(CPU.CPUArch.arm64); + List dummyHosts = Arrays.asList(host1, host2); + doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull()); + List> result = hostDao.listDistinctHypervisorArchTypes(zoneId); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(Hypervisor.HypervisorType.XenServer, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + assertEquals(Hypervisor.HypervisorType.KVM, result.get(1).first()); + assertEquals(CPU.CPUArch.arm64, result.get(1).second()); + } + + @Test + public void testListDistinctHypervisorArchTypes_WithoutZone() { + Long zoneId = null; + HostVO host1 = mock(HostVO.class); + when(host1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.VMware); + when(host1.getArch()).thenReturn(CPU.CPUArch.amd64); + List dummyHosts = Collections.singletonList(host1); + doReturn(dummyHosts).when(hostDao).search(any(SearchCriteria.class), isNull()); + List> result = hostDao.listDistinctHypervisorArchTypes(zoneId); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(Hypervisor.HypervisorType.VMware, result.get(0).first()); + assertEquals(CPU.CPUArch.amd64, result.get(0).second()); + } + + @Test + public void testListDistinctArchTypes() { + Long clusterId = 1L; + List mockResults = List.of(CPU.CPUArch.amd64, CPU.CPUArch.arm64); + HostVO host = mock(HostVO.class); + GenericSearchBuilder sb = mock(GenericSearchBuilder.class); + when(sb.entity()).thenReturn(host); + SearchCriteria sc = mock(SearchCriteria.class); + when(sb.create()).thenReturn(sc); + doReturn(sb).when(hostDao).createSearchBuilder(String.class); + doReturn(mockResults.stream().map(h -> h.getType()).collect(Collectors.toList())).when(hostDao) + .customSearch(any(SearchCriteria.class), any()); + List hypervisorTypes = hostDao.listDistinctArchTypes(clusterId); + assertEquals(mockResults, hypervisorTypes); + Mockito.verify(sc).setParameters("clusterId", clusterId); + Mockito.verify(sc).setParameters("type", Host.Type.Routing); + } } diff --git a/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVOTest.java b/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVOTest.java new file mode 100644 index 00000000000..8f84ecac3dd --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/network/dao/LoadBalancerVOTest.java @@ -0,0 +1,45 @@ +// 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.network.dao; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class LoadBalancerVOTest { + @Test + public void testSetCidrList() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(); + String cidrList = "192.168.1.0/24,10.0.0.0/16"; + loadBalancer.setCidrList(cidrList); + assertEquals(cidrList, loadBalancer.getCidrList()); + } + + @Test + public void testSetCidrListEmpty() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(); + loadBalancer.setCidrList(""); + assertEquals("", loadBalancer.getCidrList()); + } + + @Test + public void testSetCidrListNull() { + LoadBalancerVO loadBalancer = new LoadBalancerVO(); + loadBalancer.setCidrList(null); + assertNull(loadBalancer.getCidrList()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java new file mode 100644 index 00000000000..3c8e4c046ae --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/storage/dao/VMTemplateDaoImplTest.java @@ -0,0 +1,340 @@ +// 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.storage.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.cpu.CPU; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.Storage; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VMTemplateZoneVO; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@RunWith(MockitoJUnitRunner.class) +public class VMTemplateDaoImplTest { + + @Mock + HostDao hostDao; + + @Mock + VMTemplateZoneDao templateZoneDao; + + @Spy + @InjectMocks + VMTemplateDaoImpl templateDao = new VMTemplateDaoImpl(); + + @Test + public void testFindLatestTemplateByName_ReturnsTemplate() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault()); + assertNotNull("Expected a non-null template", result); + assertEquals("Expected the returned template to be the first element", expectedTemplate, result); + } + + @Test + public void testFindLatestTemplateByName_ReturnsNullWhenNoTemplateFound() { + List emptyList = Collections.emptyList(); + doReturn(emptyList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", CPU.CPUArch.getDefault()); + assertNull("Expected null when no templates are found", result); + } + + @Test + public void testFindLatestTemplateByName_NullArch() { + VMTemplateVO expectedTemplate = new VMTemplateVO(); + List returnedList = Collections.singletonList(expectedTemplate); + doReturn(returnedList).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO result = templateDao.findLatestTemplateByName("test", null); + assertNotNull("Expected a non-null template even if arch is null", result); + assertEquals("Expected the returned template to be the first element", expectedTemplate, result); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_PreferredProvided() { + VMTemplateVO templatePreferred = Mockito.mock(VMTemplateVO.class); + when(templatePreferred.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO templateOther = Mockito.mock(VMTemplateVO.class); + when(templateOther.getArch()).thenReturn(CPU.CPUArch.arm64); + + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), templatePreferred); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), templateOther); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, + CPU.CPUArch.amd64.getType()); + assertEquals(2, sortedList.size()); + assertEquals(templatePreferred, sortedList.get(0)); + assertEquals(templateOther, sortedList.get(1)); + } + + @Test + public void testGetSortedTemplatesListWithPreferredArch_NoPreferred() { + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getId()).thenReturn(1L); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getId()).thenReturn(2L); + Map, VMTemplateVO> uniqueTemplates = new HashMap<>(); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64), template1); + uniqueTemplates.put(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64), template2); + List sortedList = templateDao.getSortedTemplatesListWithPreferredArch(uniqueTemplates, ""); + assertEquals(2, sortedList.size()); + assertEquals(template2, sortedList.get(0)); + assertEquals(template1, sortedList.get(1)); + } + + @Test + public void testFindSystemVMReadyTemplates() { + long zoneId = 1L; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + String preferredArch = CPU.CPUArch.arm64.getType(); + List> availableHypervisors = new ArrayList<>(); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.amd64)); + availableHypervisors.add(new Pair<>(Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64)); + doReturn(availableHypervisors).when(hostDao).listDistinctHypervisorArchTypes(zoneId); + VMTemplateVO template1 = Mockito.mock(VMTemplateVO.class); + when(template1.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template1.getArch()).thenReturn(CPU.CPUArch.amd64); + VMTemplateVO template2 = Mockito.mock(VMTemplateVO.class); + when(template2.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + when(template2.getArch()).thenReturn(CPU.CPUArch.arm64); + List templatesFromDb = Arrays.asList(template1, template2); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + templateDao.readySystemTemplateSearch = sb; + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + List result = templateDao.findSystemVMReadyTemplates(zoneId, hypervisorType, preferredArch); + assertNotNull(result); + assertEquals(2, result.size()); + assertEquals(template2, result.get(0)); + assertEquals(template1, result.get(1)); + } + + @Test + public void testFindRoutingTemplates() { + Hypervisor.HypervisorType hType = Hypervisor.HypervisorType.KVM; + String templateName = "TestRouting"; + String preferredArch = CPU.CPUArch.amd64.getType(); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + when(template.getArch()).thenReturn(CPU.CPUArch.amd64); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + SearchBuilder sb = mock(SearchBuilder.class); + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + templateDao.tmpltTypeHyperSearch2 = sb; + List result = templateDao.findRoutingTemplates(hType, templateName, preferredArch); + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(template, result.get(0)); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_Found() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.amd64; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + List templatesFromDb = Collections.singletonList(template); + doReturn(templatesFromDb).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNotNull(result); + assertEquals(template, result); + } + + @Test + public void testFindLatestTemplateByTypeAndHypervisorAndArch_NotFound() { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + CPU.CPUArch arch = CPU.CPUArch.x86; + Storage.TemplateType type = Storage.TemplateType.SYSTEM; + doReturn(Collections.emptyList()).when(templateDao).listBy(any(), any()); + VMTemplateVO result = templateDao.findLatestTemplateByTypeAndHypervisorAndArch(hypervisorType, arch, type); + assertNull(result); + } + + private void mockTemplateZoneJoin() { + VMTemplateZoneVO templateZoneVO = mock(VMTemplateZoneVO.class); + SearchBuilder templateZoneVOSearchBuilder = mock(SearchBuilder.class); + when(templateZoneVOSearchBuilder.entity()).thenReturn(templateZoneVO); + when(templateZoneDao.createSearchBuilder()).thenReturn(templateZoneVOSearchBuilder); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithDataCenterId() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutDataCenterId() { + Long dataCenterId = null; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = false; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutArch() { + Long dataCenterId = 1L; + CPU.CPUArch arch = null; + Boolean isIso = true; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListTemplateIsoByArchAndZone_WithoutIsIso() { + Long dataCenterId = 1L; + CPU.CPUArch arch = CPU.CPUArch.getDefault(); + Boolean isIso = null; + VMTemplateVO templateVO = mock(VMTemplateVO.class); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + when(searchBuilder.entity()).thenReturn(templateVO); + SearchCriteriasearchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.create()).thenReturn(searchCriteria); + mockTemplateZoneJoin(); + doReturn(new ArrayList<>()).when(templateDao).customSearch(searchCriteria, null); + List result = templateDao.listTemplateIsoByArchVnfAndZone(dataCenterId, arch, isIso, false); + assertNotNull(result); + verify(searchBuilder, times(1)).select(null, SearchCriteria.Func.DISTINCT, templateVO.getGuestOSId()); + verify(searchBuilder, times(1)).and(eq("state"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("type"), any(), eq(SearchCriteria.Op.IN)); + verify(searchBuilder, times(1)).and(eq("arch"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.NEQ)); + verify(searchBuilder, never()).and(eq("isIso"), any(), eq(SearchCriteria.Op.EQ)); + verify(searchBuilder, times(1)).join(eq("templateZoneSearch"), any(), any(), any(), eq(JoinBuilder.JoinType.INNER)); + verify(templateDao, times(1)).customSearch(searchCriteria, null); + } + + @Test + public void testListIdsByExtensionId_ReturnsIds() { + long extensionId = 42L; + List expectedIds = Arrays.asList(1L, 2L, 3L); + GenericSearchBuilder searchBuilder = mock(GenericSearchBuilder.class); + SearchCriteria searchCriteria = mock(SearchCriteria.class); + when(templateDao.createSearchBuilder(Long.class)).thenReturn(searchBuilder); + when(searchBuilder.entity()).thenReturn(mock(VMTemplateVO.class)); + when(searchBuilder.create()).thenReturn(searchCriteria); + doReturn(expectedIds).when(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + List result = templateDao.listIdsByExtensionId(extensionId); + assertEquals(expectedIds, result); + verify(searchCriteria).setParameters("extensionId", extensionId); + verify(templateDao).customSearchIncludingRemoved(eq(searchCriteria), isNull()); + } + + @Test + public void testFindSystemVMReadyTemplate() { + Long zoneId = 1L; + VMTemplateVO systemVmTemplate1 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate1.getArch()).thenReturn(CPU.CPUArch.x86); + VMTemplateVO systemVmTemplate2 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate2.getArch()).thenReturn(CPU.CPUArch.x86); + VMTemplateVO systemVmTemplate3 = mock(VMTemplateVO.class); + Mockito.when(systemVmTemplate3.getArch()).thenReturn(CPU.CPUArch.arm64); + Mockito.when(systemVmTemplate3.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.KVM); + List templates = Arrays.asList(systemVmTemplate1, systemVmTemplate2, systemVmTemplate3); + Mockito.when(hostDao.listDistinctHypervisorTypes(zoneId)).thenReturn(Arrays.asList(Hypervisor.HypervisorType.KVM)); + SearchBuilder sb = mock(SearchBuilder.class); + templateDao.readySystemTemplateSearch = sb; + when(sb.create()).thenReturn(mock(SearchCriteria.class)); + doReturn(templates).when(templateDao).listBy(any(SearchCriteria.class), any(Filter.class)); + VMTemplateVO readyTemplate = templateDao.findSystemVMReadyTemplate(zoneId, Hypervisor.HypervisorType.KVM, CPU.CPUArch.arm64.getType()); + Assert.assertEquals(CPU.CPUArch.arm64, readyTemplate.getArch()); + } +} diff --git a/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java new file mode 100644 index 00000000000..dceb8e07b07 --- /dev/null +++ b/engine/schema/src/test/java/com/cloud/upgrade/SystemVmTemplateRegistrationTest.java @@ -0,0 +1,427 @@ +// 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 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 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 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 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 - + + @@ -58,7 +109,7 @@ - +
@@ -73,18 +124,18 @@ + title="Move/Drag viewport"> - + + title="Show extra keys">
- + title="Full screen"> @@ -255,10 +306,10 @@
-
- +
+
- + @@ -273,7 +324,7 @@ - +
@@ -288,7 +339,7 @@ - +
@@ -298,21 +349,21 @@ The server has provided the following identifying information:
- Fingerprint: + Fingerprint:
Please verify that the information is correct and press "Approve". Otherwise press "Reject".
-
- - +
+ +
- +
@@ -326,17 +377,17 @@
-
- +
+
- +
- +
diff --git a/systemvm/agent/noVNC/vnc_lite.html b/systemvm/agent/noVNC/vnc_lite.html index 1eea4602222..8e6aaeabdec 100644 --- a/systemvm/agent/noVNC/vnc_lite.html +++ b/systemvm/agent/noVNC/vnc_lite.html @@ -7,7 +7,7 @@ This is a self-contained file which doesn't import WebUtil or external CSS. - Copyright (C) 2019 The noVNC Authors + Copyright (C) 2019 The noVNC authors noVNC is licensed under the MPL 2.0 (see LICENSE.txt) This file is licensed under the 2-Clause BSD license (see LICENSE.txt). @@ -87,7 +87,7 @@ // When this function is called, the server requires // credentials to authenticate function credentialsAreRequired(e) { - const password = prompt("Password Required:"); + const password = prompt("Password required:"); rfb.sendCredentials({ password: password }); } @@ -119,20 +119,14 @@ // query string. If the variable isn't defined in the URL // it returns the default value instead. function readQueryVariable(name, defaultValue) { - // A URL with a query parameter can look like this (But will most probably get logged on the http server): + // A URL with a query parameter can look like this: // https://www.example.com?myqueryparam=myvalue // - // For privacy (Using a hastag #, the parameters will not be sent to the server) - // the url can be requested in the following way: - // https://www.example.com#myqueryparam=myvalue&password=secreatvalue - // - // Even Mixing public and non public parameters will work: - // https://www.example.com?nonsecretparam=example.com#password=secreatvalue - // // Note that we use location.href instead of location.search // because Firefox < 53 has a bug w.r.t location.search const re = new RegExp('.*[?&]' + name + '=([^&#]*)'), - match = ''.concat(document.location.href, window.location.hash).match(re); + match = document.location.href.match(re); + if (typeof defaultValue === 'undefined') { defaultValue = null; } if (match) { diff --git a/systemvm/agent/scripts/run-proxy.sh b/systemvm/agent/scripts/run-proxy.sh old mode 100644 new mode 100755 diff --git a/systemvm/agent/scripts/ssvm-check.sh b/systemvm/agent/scripts/ssvm-check.sh old mode 100644 new mode 100755 diff --git a/systemvm/agent/scripts/utils.sh b/systemvm/agent/scripts/utils.sh old mode 100644 new mode 100755 diff --git a/systemvm/agent/ui/viewer.ftl b/systemvm/agent/ui/viewer.ftl index e6b12071111..fef0d7e0b68 100644 --- a/systemvm/agent/ui/viewer.ftl +++ b/systemvm/agent/ui/viewer.ftl @@ -18,10 +18,10 @@ under the License. --> - - - - + + + + ${title} @@ -44,7 +44,7 @@ under the License.
-
- diff --git a/ui/public/locales/ar.json b/ui/public/locales/ar.json index 2cf51c2c385..e8e4db66cb5 100644 --- a/ui/public/locales/ar.json +++ b/ui/public/locales/ar.json @@ -14,12 +14,12 @@ "label.account.specific": "Account-Specific", "label.accounts": "Accounts", "label.accounttype": "Account Type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Name", +"label.acl.rule.name": "ACL Name", "label.acquire.new.ip": "Acquire New IP", "label.acquire.new.secondary.ip": "Acquire new secondary IP", "label.action": "Action", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Active Sessions", "label.add": "Add", "label.add.account": "Add Account", -"label.add.acl": "\u0625\u0636\u0627\u0641\u0629 ACL", -"label.add.acl.list": "Add ACL List", +"label.add.acl.rule": "\u0625\u0636\u0627\u0641\u0629 ACL", +"label.add.acl": "Add ACL", "label.add.affinity.group": "Add new affinity group", "label.add.baremetal.dhcp.device": "Add Baremetal DHCP Device", "label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller", @@ -142,12 +142,12 @@ "label.add.ip.range": "Add IP Range", "label.add.isolated.network": "Add Isolated Network", "label.add.ldap.account": "Add LDAP account", -"label.add.list.name": "ACL List Name", +"label.add.acl.name": "ACL Name", "label.add.more": "Add More", "label.add.netscaler.device": "Add Netscaler device", "label.add.network": "Add Network", "label.add.network.acl": "\u0625\u0636\u0627\u0641\u0629 \u0634\u0628\u0643\u0629 ACL", -"label.add.network.acl.list": "Add Network ACL List", +"label.add.network.acl": "Add Network ACL", "label.add.network.offering": "Add network offering", "label.add.new.gateway": "\u0623\u0636\u0641 \u0628\u0648\u0627\u0628\u0629 \u062c\u062f\u064a\u062f\u0629", "label.add.new.tier": "\u0625\u0636\u0627\u0641\u0629 \u0637\u0628\u0642\u0629 \u062c\u062f\u064a\u062f\u0629", @@ -334,7 +334,7 @@ "label.default.use": "Default Use", "label.default.view": "\u0637\u0631\u064a\u0642\u0629 \u0627\u0644\u0639\u0631\u0636 \u0627\u0644\u0627\u0641\u062a\u0631\u0627\u0636\u064a\u0629", "label.delete": "Delete", -"label.delete.acl.list": "Delete ACL List", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "Delete Affinity Group", "label.delete.alerts": "Delete alerts", "label.delete.bigswitchbcf": "Remove BigSwitch BCF Controller", @@ -419,7 +419,7 @@ "label.dpd": "\u0643\u0634\u0641 \u0627\u0644\u0642\u0631\u064a\u0646 \u0627\u0644\u0645\u0641\u0642\u0648\u062f", "label.driver": "Driver", "label.edit": "Edit", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.project.details": "\u0627\u0636\u0627\u0641\u0629 \u062a\u0641\u0627\u0635\u064a\u0644 \u0627\u0644\u0645\u0634\u0631\u0648\u0639", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "Remove VPC offering", "label.removing": "Removing", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL List", "label.required": "Required", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires Upgrade", @@ -1150,8 +1149,7 @@ "label.usehttps": "\u0627\u0633\u062a\u062e\u062f\u0645 HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "User", -"label.userdata": "Userdata", -"label.userdatal2": "User Data", +"label.user.data": "User Data", "label.username": "Username", "label.users": "Users", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts", "message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events", "message.confirm.attach.disk": "Are you sure you want to attach disk?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v", @@ -1405,7 +1403,7 @@ "message.guest.traffic.in.basic.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications.", "message.installwizard.tooltip.addpod.name": "A name for the pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "This is the IP range in the private network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway for the hosts in that pod.", diff --git a/ui/public/locales/ca.json b/ui/public/locales/ca.json index e97d11d101f..d3e417eb821 100644 --- a/ui/public/locales/ca.json +++ b/ui/public/locales/ca.json @@ -14,12 +14,12 @@ "label.account.specific": "Account-Specific", "label.accounts": "Accounts", "label.accounttype": "Account Type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Name", +"label.acl.rule.name": "ACL Name", "label.acquire.new.ip": "Acquire New IP", "label.acquire.new.secondary.ip": "Acquire new secondary IP", "label.action": "Action", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Active Sessions", "label.add": "Add", "label.add.account": "Add Account", +"label.add.acl.rule": "Add ACL rule", "label.add.acl": "Add ACL", -"label.add.acl.list": "Add ACL List", "label.add.affinity.group": "Add new affinity group", "label.add.baremetal.dhcp.device": "Add Baremetal DHCP Device", "label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller", @@ -142,12 +142,12 @@ "label.add.ip.range": "Add IP Range", "label.add.isolated.network": "Add Isolated Network", "label.add.ldap.account": "Add LDAP account", -"label.add.list.name": "ACL List Name", +"label.add.acl.name": "ACL Name", "label.add.more": "Add More", "label.add.netscaler.device": "Add Netscaler device", "label.add.network": "Add Network", "label.add.network.acl": "Add network ACL", -"label.add.network.acl.list": "Add Network ACL List", +"label.add.network.acl": "Add Network ACL", "label.add.network.offering": "Add network offering", "label.add.new.gateway": "Add new gateway", "label.add.new.tier": "Add new tier", @@ -334,7 +334,7 @@ "label.default.use": "Default Use", "label.default.view": "Default View", "label.delete": "Delete", -"label.delete.acl.list": "Delete ACL List", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "Delete Affinity Group", "label.delete.alerts": "Delete alerts", "label.delete.bigswitchbcf": "Remove BigSwitch BCF Controller", @@ -419,7 +419,7 @@ "label.dpd": "Dead Peer Detection", "label.driver": "Driver", "label.edit": "Edit", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.project.details": "Editar detalls del projecte", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "Remove VPC offering", "label.removing": "Esborrant", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL List", "label.required": "Required", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires Upgrade", @@ -1150,8 +1149,7 @@ "label.usehttps": "Use HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "User", -"label.userdata": "Userdata", -"label.userdatal2": "User Data", +"label.user.data": "User Data", "label.username": "Username", "label.users": "Users", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts", "message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events", "message.confirm.attach.disk": "Are you sure you want to attach disk?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v", @@ -1405,7 +1403,7 @@ "message.guest.traffic.in.basic.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications.", "message.installwizard.tooltip.addpod.name": "Un nom per al pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Aquest \u00e9s el rang IP a la xarxa privada que el CloudStack fa servir per administrar MVs per al Secondary Storage i Proxy de consoles. Aquestes IP es prenen de la mateixa sub-xarxa que els servidors de virtualitzaci\u00f3.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "La passarel\u00b7la per als amfitrions en aquest pot.", diff --git a/ui/public/locales/de_DE.json b/ui/public/locales/de_DE.json index abf471ae17d..fd29ac3eb54 100644 --- a/ui/public/locales/de_DE.json +++ b/ui/public/locales/de_DE.json @@ -26,12 +26,12 @@ "label.account.specific": "Besonderheiten des Benutzerkontos", "label.accounts": "Benutzerkonten", "label.accounttype": "Benutzerkontotyp", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL-Kennung", -"label.acl.list.rules": "ACL-Listenregeln", +"label.acl.rules": "ACL-Listenregeln", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL-Name", +"label.acl.rule.name": "ACL-Name", "label.acquire.new.ip": "Neue IP erwerben", "label.acquire.new.secondary.ip": "Neue sekundäre IP anfordern", "label.acquiring.ip": "IP anfordern", @@ -144,8 +144,8 @@ "label.activeviewersessions": "Aktive Sitzungen", "label.add": "Hinzufügen", "label.add.account": "Konto hinzufügen", -"label.add.acl": "ACL hinzufügen", -"label.add.acl.list": "ACL-Liste hinzufügen", +"label.add.acl.rule": "ACL hinzufügen", +"label.add.acl": "ACL-Liste hinzufügen", "label.add.affinity.group": "Neue Affinitätsgruppe hinzufügen", "label.add.baremetal.dhcp.device": "Baremetal DHCP-Gerät hinzufügen", "label.add.bigswitchbcf.device": "Füge BigSwitch BCF Controller hinzu", @@ -169,12 +169,12 @@ "label.add.isolated.network": "Isoliertes Netzwerk hinzufügen", "label.add.kubernetes.cluster": "Kubernetes Cluster hinzufügen", "label.add.ldap.account": "LDAP-Konto hinzufügen", -"label.add.list.name": "ACL-Listename", +"label.add.acl.name": "ACL-Listename", "label.add.more": "Mehr hinzufügen", "label.add.netscaler.device": "Netscaler-Gerät hinzufügen", "label.add.network": "Netzwerk hinzufügen", "label.add.network.acl": "Netzwerk-ACL hinzufügen", -"label.add.network.acl.list": "Netzwerk-ACL-Liste hinzufügen", +"label.add.network.acl": "Netzwerk-ACL-Liste hinzufügen", "label.add.network.offering": "Netzwerkangebot hinzufügen", "label.add.new.gateway": "Neues Gateway hinzufügen", "label.add.new.tier": "Neue Ebene hinzufügen", @@ -417,7 +417,7 @@ "label.default.view": "Standardansicht", "label.defaultnetwork": "Standard-Netzwerk", "label.delete": "Löschen", -"label.delete.acl.list": "ACL-Liste ersetzen", +"label.delete.acl": "ACL-Liste ersetzen", "label.delete.affinity.group": "Affinitätsgruppe entfernen", "label.delete.alerts": "Alarme löschen", "label.delete.backup": "Backup löschen", @@ -525,7 +525,7 @@ "label.driver": "Treiber", "label.dynamicscalingenabled": "Dynamische Skalierung aktiviert", "label.edit": "Bearbeiten", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "ACL-Regel bearbeiten", "label.edit.project.details": "Projektdetails bearbeiten", "label.edit.role": "Rolle bearbeiten", @@ -948,7 +948,7 @@ "label.netscaler.vpx": "NetScaler VPX LoadBalancer", "label.network": "Netzwerk", "label.network.acl": "Netzwerk-ACL", -"label.network.acl.lists": "Netzwerk ACL Listen", +"label.network.acls": "Netzwerk ACL Listen", "label.network.addvm": "Netzwerk zur VM hinzufügen", "label.network.desc": "Netzwerkbeschreibung", "label.network.domain": "Netzwerk-Domain", @@ -1198,7 +1198,6 @@ "label.remove.vpc.offering": "VPC-Angebot entfernen", "label.removing": "am Entfernen", "label.replace.acl": "ACL ersetzen", -"label.replace.acl.list": "ACL-Liste ersetzen", "label.report.bug": "Fehler melden", "label.required": "Erforderlich", "label.requireshvm": "HVM", @@ -1492,8 +1491,7 @@ "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Benutzer", "label.user.conflict": "Konflikt", -"label.userdata": "Benutzerdaten", -"label.userdatal2": "Benutzerdaten", +"label.user.data": "Benutzerdaten", "label.username": "Benutzername", "label.users": "Benutzer", "label.usersource": "Benutzertyp", @@ -1733,7 +1731,7 @@ "message.confirm.archive.selected.alerts": "Bitte bestätigen Sie, dass Sie die ausgewählten Alarme archivieren möchten", "message.confirm.archive.selected.events": "Bitte bestätigen Sie, dass Sie die ausgewählten Vorgänge archivieren möchten", "message.confirm.attach.disk": "Sind Sie sicher, dass Sie eine Platte hinzufügen möchten?", -"message.confirm.delete.acl.list": "Sind Sie sicher, dass Sie diese ACL-Liste löschen möchten?", +"message.confirm.delete.acl": "Sind Sie sicher, dass Sie diese ACL-Liste löschen möchten?", "message.confirm.delete.bigswitchbcf": "Bitte bestätigen Sie, dass Sie diesen BigSwitch BCF Controller löschen möchten", "message.confirm.delete.brocadevcs": "Bitte bestätigen Sie, dass Sie Brocade Vcs Switch löschen möchten", "message.confirm.delete.ciscoasa1000v": "Bitte bestätigen Sie, dass Sie CiscoASA1000v löschen möchten", @@ -1954,7 +1952,7 @@ "message.host.dedicated": "Host dediziert", "message.host.dedication.released": "Host-Dedizierung freigegeben", "message.info.cloudian.console": "Cloudian Management Konsole sollte ein anderes Fenster öffnen", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ ist eine Software-Plattform welche Rechenressourcen zusammenfasst, um öffentliche, private oder hybride \"Infrastructure as a Service\" (IaaS) Clouds zu bauen. CloudStack™ verwaltet das Netzwerk-, Speicher- und Computingknoten was eine Cloud-Infrastruktur ausmacht. Benutzen Sie CloudStack™ um Computing-Umgebungen zu erstellen, verwalten und zu konfigurieren.

Neben dem Erweitern von individuellen virtuellen Maschinenabbilder auf auf Standardhardware bietet CloudStack™ einen schlüsselfertigen Cloud Infrastruktur-Software-Stack für die Bereitstellung von virtueller Rechenzentren as a Service – Liefert alle wesentlichen Komponenten für das Bauen, Bereitstellen und Verwalten von multi-tier- und mandantenfähigen Cloud-Anwendungen. Open-Source sowie Premium-Versionen sind verfügbar, mit nahezu identischen Features.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ ist eine Software-Plattform welche Rechenressourcen zusammenfasst, um öffentliche, private oder hybride \"Infrastructure as a Service\" (IaaS) Clouds zu bauen. CloudStack™ verwaltet das Netzwerk-, Speicher- und Computingknoten was eine Cloud-Infrastruktur ausmacht. Benutzen Sie CloudStack™ um Computing-Umgebungen zu erstellen, verwalten und zu konfigurieren.

Neben dem Erweitern von individuellen virtuellen Maschinenabbilder auf auf Standardhardware bietet CloudStack™ einen schlüsselfertigen Cloud Infrastruktur-Software-Stack für die Bereitstellung von virtueller Rechenzentren as a Service – Liefert alle wesentlichen Komponenten für das Bauen, Bereitstellen und Verwalten von multi-tier- und mandantenfähigen Cloud-Anwendungen.", "message.installwizard.tooltip.addpod.name": "Der Name für den pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Dies ist der IP-Bereich im privaten Netzwerk, welches CloudStack verwendet um Sekundärspeicher-VMs und Konsolen-Proxies zu verwalten. Diese IP-Adressen werden vom selben Subnetz genommen wie Computing-Server.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "Das Gateways für die Hosts des pod", diff --git a/ui/public/locales/el_GR.json b/ui/public/locales/el_GR.json index 7b7f4b29e12..6b939f52029 100644 --- a/ui/public/locales/el_GR.json +++ b/ui/public/locales/el_GR.json @@ -33,10 +33,10 @@ "label.access.kubernetes.nodes": "Πρόσβαση στους κόμβους Κυβερνητών", "label.acl.export": "Εξαγωγή Λίστας Πρόσβασης", "label.acl.id": "Αναγνωριστικό Λίστας Πρόσβασης", -"label.acl.list.rules": "Κανόνες λίστας Πρόσωασης", +"label.acl.rules": "Κανόνες λίστας Πρόσωασης", "label.acl.reason.description": "Εισαγάγετε αιτιολογία για τον κανόνα", "label.aclid": "Λίστα πρόσβασης", -"label.aclname": "Όνομα Λίστας Πρόσβασης", +"label.acl.rule.name": "Όνομα Λίστας Πρόσβασης", "label.acquire.new.ip": "Απόκτηση νέας διεύθυνση IP", "label.acquire.new.secondary.ip": "Απόκτηση νέας δευτερεύουσας διεύθυνσης IP", "label.acquiring.ip": "Απόδοση IP", @@ -173,7 +173,7 @@ "label.action.unmanage.virtualmachine": "Μη διαχείριση εικονικής μηχανής", "label.action.update.offering.access": "Ενημέρωση πρόσβασης για προσφορές", "label.action.update.resource.count": "Ενημέρωση πλήθους πόρων", -"label.action.userdata.reset": "Επαναφορά δεδομένων χρήστη", +"label.action.user.data.reset": "Επαναφορά δεδομένων χρήστη", "label.action.vmsnapshot.create": "Λήψη στιγμιότυπου εικονικής μηχανής", "label.action.vmsnapshot.delete": "Διαγραφή στιγμιότυπου εικονικής μηχανής", "label.action.vmsnapshot.revert": "Επαναφορά στο στιγμιότυπο εικονικής μηχανής", @@ -183,8 +183,8 @@ "label.activeviewersessions": "Ενεργές περίοδοι λειτουργίας", "label.add": "Προσθήκη", "label.add.account": "Προσθήκη λογαριασμού", -"label.add.acl": "Προσθήκη εγγραφής στην λίστα πρόσβασης", -"label.add.acl.list": "Προσθήκη λίστας πρόσβασης", +"label.add.acl.rule": "Προσθήκη εγγραφής στην λίστα πρόσβασης", +"label.add.acl": "Προσθήκη λίστας πρόσβασης", "label.add.affinity.group": "Προσθήκη νέας ομάδας συνάφειας", "label.add.baremetal.dhcp.device": "Προσθήκη συσκευής DHCP baremetal", "label.add.bigswitchbcf.device": "Προσθήκη ελεγκτή BCF BigSwitch", @@ -209,12 +209,12 @@ "label.add.isolated.network": "Προσθήκη απομονωμένου δικτύου", "label.add.kubernetes.cluster": "Προσθήκη ομάδας Κυβερνητών", "label.add.ldap.account": "Προσθήκη λογαριασμού LDAP", -"label.add.list.name": "Όνομα λίστας Πρόσβασης", +"label.add.acl.name": "Όνομα λίστας Πρόσβασης", "label.add.more": "Προσθήκη περισσότερων", "label.add.netscaler.device": "Προσθήκη συσκευής Netsccaler", "label.add.network": "Προσθήκη δικτύου", "label.add.network.acl": "Προσθήκη εγγραφής πρόσβασης δικτύου", -"label.add.network.acl.list": "Προσθήκη λίστας πρόσβασης δικτύου", +"label.add.network.acl": "Προσθήκη λίστας πρόσβασης δικτύου", "label.add.network.offering": "Προσθήκη προσφοράς υπηρεσίας δικτύου", "label.add.network.permission": "Προσθήκη δικαιωμάτων δικτύου", "label.add.new.gateway": "Προσθήκη νέας πύλης", @@ -516,7 +516,7 @@ "label.default.view": "Προεπιλεγμένη προβολή", "label.defaultnetwork": "Προεπιλεγμένο δίκτυο", "label.delete": "Διαγραφή", -"label.delete.acl.list": "Διαγραφή λίστας πρόσβασης", +"label.delete.acl": "Διαγραφή λίστας πρόσβασης", "label.delete.affinity.group": "Διαγραφή ομάδας συνάφειας", "label.delete.alerts": "Διαγραφή ειδοποιήσεων", "label.delete.backup": "Διαγραφή αντιγράφου ασφαλείας", @@ -649,7 +649,7 @@ "label.dynamicscalingenabled": "Δυναμική αναπροσαρμογή Ενεργοποίημένη", "label.dynamicscalingenabled.tooltip": "Η εικονική μηχανή μπορεί να αναπροσαρμόζεται δυναμικά μόνο αν το πρότυπο, η προσφερόμενη υπηρεσία και οι γενικές ρυθμίσεις έχουν την επιλογή δυναμικής αναπροσαρμογής ενεργοποιήμενη.", "label.edit": "Επεξεργασία", -"label.edit.acl.list": "Επεξεργασία λίστας Πρόσβασης", +"label.edit.acl": "Επεξεργασία λίστας Πρόσβασης", "label.edit.acl.rule": "Επεξεργασία κανόνα Λίστας Πρόσβασης", "label.edit.project.details": "Επεξεργασία λεπτομερειών έργου", "label.edit.project.role": "Επεξεργασία ρόλου έργου", @@ -1142,7 +1142,7 @@ "label.netscaler.vpx": "Εξισορρόπηση φόρτου VPX του NetScaler", "label.network": "Δίκτυο", "label.network.acl": "ACL δικτύου", -"label.network.acl.lists": "Λίστες Λίστα Πρόσβασης δικτύου", +"label.network.acls": "Λίστες Λίστα Πρόσβασης δικτύου", "label.network.addvm": "Προσθήκη δικτύου για την εικονική μηχανή", "label.network.addvm": "Προσθήκη δικτύου σε εικονική μηχανή", "label.network.desc": "Δίκτυο Desc", @@ -1436,7 +1436,6 @@ "label.remove.vpc.offering": "Κατάργηση προσφοράς υπηρεσίας Εικον. Ιδιωτ. Νέφους", "label.removing": "Αφαίρεση", "label.replace.acl": "Αντικατάσταση λίστας Πρόσβασης", -"label.replace.acl.list": "Αντικατάσταση λίστας Πρόσβασης", "label.report.bug": "Θέμα αναφοράς", "label.required": "Απαιτείται", "label.requireshvm": "HVM", @@ -1455,7 +1454,7 @@ "label.reset.config.value": "Επαναφορά στις τιμές προεπιλογής", "label.reset.ssh.key.pair": "Επαναφορά ζεύγους κλειδιών SSH", "label.reset.to.default": "Επαναφορά στην τιμή προεπιλογής", -"label.reset.userdata.on.vm": "Επαναφορά δεδομένων χρήστη της εικονικής μηχανής", +"label.reset.user.data.on.vm": "Επαναφορά δεδομένων χρήστη της εικονικής μηχανής", "label.reset.vpn.connection": "Επαναφορά σύνδεσης Εικον. Ιδιωτ. Δικτύου", "label.resource": "Πόρος", "label.resource.limit.exceeded": "Υπέρβαση ορίου πόρων", @@ -1811,19 +1810,17 @@ "label.usenewdiskoffering": "Αντικατάσταση προσφοράς υπηρεσίας δίσκου;", "label.user": "Χρήστη", "label.user.conflict": "Σύγκρουση", -"label.user.data": "Δεδομένα χρηστών", -"label.userdata": "Δεδομένα χρήστη", -"label.userdata.do.append": "Πρόσθεση δεδομένων χρήστη", -"label.userdata.do.override": "Παράκαμψη δεδομένων χρήστη", -"label.userdata.registered": "Εγγεγραμένα δεδομένα χρήστη", -"label.userdata.text": "Κείμενο δεδομένων χρήστη", -"label.userdatadetails": "Λεπτομέρειες δεδομένων χρήστη", -"label.userdataid": "Αναγνωριστικό δεδομένων χρήστη", -"label.userdatal2": "Δεδομένα χρήστη", -"label.userdataname": "Όνομα δεδομένων χρήστη", -"label.userdataparams": "Παράμετροι δεδομένων χρήστη", -"label.userdatapolicy": "Συνδεμένες πολιτικές δεδομένων χρήστη", -"label.userdatapolicy.tooltip": "Τα δεδομένα χρήστη που έχουν συνδεθεί στο πρότυπο μπορούν να παρακαμφθούν απο τα δεδομένα χρηστών που ορίστηκαν κατα την δημιουργία της vm. Διαλέξτε την πολιτική παράκαμψης ανάλογα με τις απαιτήσεις.", +"label.user.data": "Δεδομένα χρήστη", +"label.user.data.do.append": "Πρόσθεση δεδομένων χρήστη", +"label.user.data.do.override": "Παράκαμψη δεδομένων χρήστη", +"label.user.data.registered": "Εγγεγραμένα δεδομένα χρήστη", +"label.user.data.text": "Κείμενο δεδομένων χρήστη", +"label.user.data.details": "Λεπτομέρειες δεδομένων χρήστη", +"label.user.data.id": "Αναγνωριστικό δεδομένων χρήστη", +"label.user.data.name": "Όνομα δεδομένων χρήστη", +"label.user.data.params": "Παράμετροι δεδομένων χρήστη", +"label.user.data.policy": "Συνδεμένες πολιτικές δεδομένων χρήστη", +"label.user.data.policy.tooltip": "Τα δεδομένα χρήστη που έχουν συνδεθεί στο πρότυπο μπορούν να παρακαμφθούν απο τα δεδομένα χρηστών που ορίστηκαν κατα την δημιουργία της vm. Διαλέξτε την πολιτική παράκαμψης ανάλογα με τις απαιτήσεις.", "label.username": "Όνομα χρήστη", "label.users": "Χρήστες", "label.usersource": "Τύπος χρήστη", @@ -2116,7 +2113,7 @@ "message.confirm.attach.disk": "Είστε βέβαιοι ότι θέλετε να επισυνάψετε το δίσκο;", "message.confirm.change.offering.for.volume": "Παρακαλώ επιβεβαιώστε ότι επιθυμείτε την αλλαγή της προσφοράς δίσκου για αυτόν τον τόμο", "message.confirm.configure.ovs": "Είστε βέβαιοι ότι θέλετε να ρυθμίσετε τις παραμέτρους του Ovs;", -"message.confirm.delete.acl.list": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη λίστα Λίστα Πρόσβασης;", +"message.confirm.delete.acl": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη λίστα Λίστα Πρόσβασης;", "message.confirm.delete.bigswitchbcf": "Επιβεβαιώστε ότι θέλετε να διαγράψετε αυτόν τον ελεγκτή BigSwitch BCF", "message.confirm.delete.brocadevcs": "Επιβεβαιώστε ότι θέλετε να διαγράψετε το εναλλάκτη Brocade Vcs", "message.confirm.delete.ciscoasa1000v": "Παρακαλώ επιβεβαιώστε ότι θέλετε να διαγράψετε CiscoASA1000v", @@ -2350,7 +2347,7 @@ "message.error.upload.template": "Η αποστολή του προτύπου απέτυχε", "message.error.upload.template.description": "Μόνο ένα πρότυπο μπορεί να αποσταλεί κάθε φορά", "message.error.url": "Πληκτρολογήστε διεύθυνση URL", -"message.error.userdata": "Εισάγωγή δεδομένων χρηστών", +"message.error.user.data": "Εισάγωγή δεδομένων χρηστών", "message.error.username": "Εισάγετε το όνομα χρήστη σας", "message.error.valid.iops.range": "Εισαγεται ένα σωστό εύρος εργασίων εισαγ./εξαγ ανα δευτ.", "message.error.vcenter.datacenter": "Πληκτρολογήστε vCenter Datacenter", @@ -2385,7 +2382,7 @@ "message.installwizard.cloudstack.helptext.releasenotes": " * Σημειώσης εκδόσεων:\t ", "message.installwizard.cloudstack.helptext.survey": " * Απαντήστε στην συλλογή στοιχείων:\t ", "message.installwizard.cloudstack.helptext.website": " * Σελίδα έργου:\t ", -"message.installwizard.copy.whatiscloudstack": "Το CloudStack™ είναι μια πλατφόρμα λογισμικού που συγκεντρώνει υπολογιστικούς πόρους για τη δημιουργία δημόσιων, ιδιωτικών και υβριδικών υποδομων ως υπηρεσίας (IaaS) cloud. Το CloudStack™ διαχειρίζεται τους κόμβους δικτύου, αποθήκευσης και υπολογισμού που αποτελούν μια υποδομή cloud. Χρησιμοποιήστε το CloudStack™ για να αναπτύξετε, να διαχειριστείτε και να ρυθμίσετε τις παραμέτρους περιβαλλόντων Cloud.\n\nΤο CloudStack™ παρέχει μια στοίβα λογισμικού υποδομής cloud για την παροχή εικονικών κέντρων δεδομένων ως υπηρεσία - παρέχοντας όλα τα βασικά στοιχεία για τη δημιουργία, την ανάπτυξη και τη διαχείριση εφαρμογών cloud πολλαπλών επιπέδων και πολλών tenants. Και οι εκδόσεις ανοιχτού κώδικα και Premium είναι διαθέσιμες, με την έκδοση ανοιχτού κώδικα να προσφέρει σχεδόν πανομοιότυπα χαρακτηριστικά.", +"message.installwizard.copy.whatiscloudstack": "Το CloudStack™ είναι μια πλατφόρμα λογισμικού που συγκεντρώνει υπολογιστικούς πόρους για τη δημιουργία δημόσιων, ιδιωτικών και υβριδικών υποδομων ως υπηρεσίας (IaaS) cloud. Το CloudStack™ διαχειρίζεται τους κόμβους δικτύου, αποθήκευσης και υπολογισμού που αποτελούν μια υποδομή cloud. Χρησιμοποιήστε το CloudStack™ για να αναπτύξετε, να διαχειριστείτε και να ρυθμίσετε τις παραμέτρους περιβαλλόντων Cloud.\n\nΤο CloudStack™ παρέχει μια στοίβα λογισμικού υποδομής cloud για την παροχή εικονικών κέντρων δεδομένων ως υπηρεσία - παρέχοντας όλα τα βασικά στοιχεία για τη δημιουργία, την ανάπτυξη και τη διαχείριση εφαρμογών cloud πολλαπλών επιπέδων και πολλών tenants.", "message.installwizard.tooltip.addpod.name": "Ένα όνομα για το pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Αυτή είναι η περιοχή IP στο ιδιωτικό δίκτυο που χρησιμοποιεί το CloudStack για τη διαχείριση των εικονικής μηχανής δευτερεύουσας αποθήκευσης και των εικονικής μηχανής διακομιστή μεσολάβησης κονσόλας. Αυτές οι διευθύνσεις IP λαμβάνονται από το ίδιο υποδίκτυο με τους διακομιστές υπολογιστών.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "Η πύλη για τους κεντρικους υπολογιστές σε αυτό το pod.", @@ -2689,7 +2686,6 @@ "message.volume.state.uploaderror": "Η αποστολή τόμου αντιμετώπισε κάποιο σφάλμα", "message.volume.state.uploadinprogress": "Η αποστολή τόμου βρίσκεται σε εξέλιξη", "message.volume.state.uploadop": "Η λειτουργία αποστολής τόμου βρίσκεται σε εξέλιξη ή, εν ολίγοις, ο τόμος βρίσκεται σε δευτερεύουσα αποθήκευση", -"message.vr.alert.upon.network.offering.creation.l2": "Επειδή εικονικοί δρομολογητές δεν χρησιμοποιούνται για δίκτυα L2 η προσφορά υπολογιστικού νέφους δεν θα χρησιμοποιηθεί", "message.vr.alert.upon.network.offering.creation.others": "Εφόσον καμία απο τις υποχρεωτικές υπηρεσίες για την δημιουργία του εικονικού δρομολογητή (VPN, DHCP, DNS, Firewall, LB, UserData, SourceNat, StaticNat, PortForwarding) δεν είναι ενεργή, ο εικονικός δρομολογητές δεν θα δημιουργθεί και η προσφορά υπολογιστικού νέςφους δεν θα χρησιμοποιηθεί.", "message.warn.filetype": "jpg, jpeg, png, bmp και svg είναι οι μόνες υποστηριζόμενες μορφές εικόνας.", "message.zone.creation.complete": "Η δημιουργία ζώνης ολοκληρώθηκε", diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 1bfc22ecb76..5136f241fb3 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1,22 +1,23 @@ { -"alert.service.domainrouter": "Domain router", +"message.delete.account.not.disabled": "Please disable the account before attempting to delete it.", +"alert.service.domainrouter": "Domain Router", "error.dedicate.bgp.peer.failed":"Failed to dedicate BGP peer", -"error.dedicate.cluster.failed": "Failed to dedicate cluster.", -"error.dedicate.host.failed": "Failed to dedicate host.", +"error.dedicate.cluster.failed": "Failed to dedicate Cluster.", +"error.dedicate.host.failed": "Failed to dedicate Host.", "error.dedicate.ipv4.subnet.failed": "Failed to dedicate IPv4 subnet.", -"error.dedicate.pod.failed": "Failed to dedicate pod.", -"error.dedicate.zone.failed": "Failed to dedicate zone.", +"error.dedicate.pod.failed": "Failed to dedicate Pod.", +"error.dedicate.zone.failed": "Failed to dedicate Zone.", "error.empty.counter.operator.threshold": "Either Counter, Operator or Threshold is empty", "error.execute.api.failed": "Failed to execute API.", "error.fetching.async.job.result": "Error encountered while fetching async job result.", "error.form.message": "There are errors in the form. Please fix them.", "error.password.not.match": "The password fields do not match", "error.release.dedicate.bgp.peer": "Failed to release dedicated BGP peer.", -"error.release.dedicate.cluster": "Failed to release dedicated cluster.", +"error.release.dedicate.cluster": "Failed to release dedicated Cluster.", "error.release.dedicate.host": "Failed to release dedicated host.", "error.release.dedicate.ipv4.subnet": "Failed to release dedicated IPv4 subnet.", -"error.release.dedicate.pod": "Failed to release dedicated pod.", -"error.release.dedicate.zone": "Failed to release dedicated zone.", +"error.release.dedicate.pod": "Failed to release dedicated Pod.", +"error.release.dedicate.zone": "Failed to release dedicated Zone.", "error.unable.to.add.setting.extraconfig": "It is not allowed to add setting for extraconfig. Please update VirtualMachine with extraconfig parameter.", "error.unable.to.proceed": "Unable to proceed. Please contact your administrator.", "firewall.close": "Firewall", @@ -29,9 +30,9 @@ "label.accept.project.invitation": "Accept project invitation", "label.access": "Access", "label.access.kubernetes.nodes": "Access Kubernetes nodes", -"label.accesskey": "Access key", -"label.access.key": "Access key", -"label.secret.key": "Secret key", +"label.accesskey": "Access Key", +"label.access.key": "Access Key", +"label.secret.key": "Secret Key", "label.apikeyaccess": "Api Key Access", "label.account": "Account", "label.account.and.security.group": "Account - security group", @@ -41,18 +42,20 @@ "label.accounts": "Accounts", "label.accountstate": "Account state", "label.accounttype": "Account type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL list rules", +"label.acl.rules": "ACL rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL name", +"label.acl.rule.name": "ACL rule name", "label.acquire.new.ip": "Acquire new IP", "label.acquire.new.secondary.ip": "Acquire new secondary IP", "label.acquiring.ip": "Acquiring IP", "label.associated.resource": "Associated resource", "label.action": "Action", -"label.action.attach.disk": "Attach disk", +"label.action.add.nodes.to.kubernetes.cluster": "Add nodes to Kubernetes cluster", +"label.action.remove.nodes.from.kubernetes.cluster": "Remove nodes from Kubernetes cluster", +"label.action.attach.disk": "Attach Disk", "label.action.attach.iso": "Attach ISO", "label.action.attach.to.instance": "Attach to Instance", "label.action.bulk.delete.egress.firewall.rules": "Bulk delete egress firewall rules", @@ -62,92 +65,96 @@ "label.action.bulk.delete.load.balancer.rules": "Bulk delete load balancer rules", "label.action.bulk.delete.portforward.rules": "Bulk delete port forward rules", "label.action.bulk.delete.routing.firewall.rules": "Bulk remove IPv4 Routing firewall rules", -"label.action.bulk.delete.snapshots": "Bulk delete snapshots", +"label.action.bulk.delete.snapshots": "Bulk delete Snapshots", "label.action.bulk.delete.templates": "Bulk delete Templates", "label.action.bulk.release.public.ip.address": "Bulk release public IP addresses", "label.action.cancel.maintenance.mode": "Cancel maintenance mode", "label.action.change.password": "Change password", "label.action.clear.webhook.deliveries": "Clear deliveries", "label.action.delete.webhook.deliveries": "Delete deliveries", -"label.action.change.primary.storage.scope": "Change primary storage scope", +"label.action.change.primary.storage.scope": "Change Primary Storage scope", "label.action.configure.stickiness": "Stickiness", +"label.action.configure.storage.access.group": "Update storage access group", "label.action.copy.iso": "Copy ISO", "label.action.copy.snapshot": "Copy Snapshot", "label.action.copy.template": "Copy Template", "label.action.create.snapshot.from.vmsnapshot": "Create Snapshot from Instance Snapshot", "label.action.create.template.from.volume": "Create Template from volume", -"label.action.create.volume": "Create volume", +"label.action.create.volume": "Create Volume", "label.action.create.volume.add": "Create and Add Volume", "label.action.delete.account": "Delete Account", "label.action.delete.backup.offering": "Delete backup offering", -"label.action.delete.cluster": "Delete cluster", -"label.action.delete.domain": "Delete domain", -"label.action.delete.egress.firewall": "Delete egress firewall rule", -"label.action.delete.firewall": "Delete firewall rule", +"label.action.delete.cluster": "Delete Cluster", +"label.action.delete.domain": "Delete Domain", +"label.action.delete.egress.firewall": "Delete Egress Firewall Rule", +"label.action.delete.firewall": "Delete Firewall Rule", "label.action.delete.interface.static.route": "Remove Tungsten Fabric interface static route", -"label.action.delete.guest.os": "Delete guest os", -"label.action.delete.guest.os.hypervisor.mapping": "Delete guest os hypervisor mapping", +"label.action.delete.gpu.card": "Delete GPU card", +"label.action.delete.guest.os": "Delete guest OS", +"label.action.delete.guest.os.category": "Delete guest OS category", +"label.action.delete.guest.os.hypervisor.mapping": "Delete guest OS hypervisor mapping", "label.action.delete.ip.range": "Delete IP range", "label.action.delete.iso": "Delete ISO", -"label.action.delete.load.balancer": "Delete load balancer rule", +"label.action.delete.load.balancer": "Delete Load Balancer Rule", "label.action.delete.network": "Delete Network", "label.action.delete.network.static.route": "Remove Tungsten Fabric Network static route", "label.action.delete.network.permission": "Delete Network permission", -"label.action.delete.node": "Delete node", +"label.action.delete.node": "Delete Node", "label.action.delete.oauth.provider": "Delete OAuth provider", "label.action.delete.physical.network": "Delete physical Network", "label.action.delete.pod": "Delete Pod", -"label.action.delete.primary.storage": "Delete primary storage", +"label.action.delete.primary.storage": "Delete Primary Storage", "label.action.delete.routing.firewall.rule": "Delete IPv4 Routing firewall rule", -"label.action.delete.secondary.storage": "Delete secondary storage", -"label.action.delete.security.group": "Delete security group", +"label.action.delete.secondary.storage": "Delete Secondary Storage", +"label.action.delete.security.group": "Delete Security Group", "label.action.delete.snapshot": "Delete Snapshot", "label.action.delete.template": "Delete Template", "label.action.delete.tungsten.router.table": "Remove Tungsten Fabric route table from Network", "label.action.delete.user": "Delete User", -"label.action.delete.volume": "Delete volume", -"label.action.delete.zone": "Delete zone", +"label.action.delete.vgpu.profile": "Delete vGPU profile", +"label.action.delete.volume": "Delete Volume", +"label.action.delete.zone": "Delete Zone", "label.action.destroy.instance": "Destroy Instance", -"label.action.destroy.systemvm": "Destroy system VM", -"label.action.destroy.volume": "Destroy volume", -"label.action.detach.disk": "Detach disk", +"label.action.destroy.systemvm": "Destroy System VM", +"label.action.destroy.volume": "Destroy Volume", +"label.action.detach.disk": "Detach Disk", "label.action.detach.iso": "Detach ISO", "label.action.disable.account": "Disable Account", -"label.action.disable.cluster": "Disable cluster", -"label.action.disable.disk.offering": "Disable disk offering", +"label.action.disable.cluster": "Disable Cluster", +"label.action.disable.disk.offering": "Disable Disk Offering", "label.action.disable.physical.network": "Disable physical Network", -"label.action.disable.pod": "Disable pod", +"label.action.disable.pod": "Disable Pod", "label.action.disable.role": "Disable Role", "label.action.disable.static.nat": "Disable static NAT", -"label.action.disable.service.offering": "Disable service offering", -"label.action.disable.system.service.offering": "Disable system service offering", +"label.action.disable.service.offering": "Disable Service Offering", +"label.action.disable.system.service.offering": "Disable System Service Offering", "label.action.disable.user": "Disable User", -"label.action.disable.zone": "Disable zone", +"label.action.disable.zone": "Disable Zone", "label.action.download.iso": "Download ISO", "label.action.download.snapshot": "Download Snapshot", "label.action.download.template": "Download Template", -"label.action.download.volume": "Download volume", +"label.action.download.volume": "Download Volume", "label.action.edit.account": "Edit Account", -"label.action.edit.domain": "Edit domain", +"label.action.edit.domain": "Edit Domain", "label.action.edit.instance": "Edit Instance", "label.action.edit.iso": "Edit ISO", "label.action.edit.nfs.options": "Edit NFS mount options", "label.action.edit.template": "Edit Template", -"label.action.edit.zone": "Edit zone", +"label.action.edit.zone": "Edit Zone", "label.action.enable.two.factor.authentication": "Enabled Two factor authentication", "label.action.verify.two.factor.authentication": "Verified Two factor authentication", "label.action.enable.account": "Enable Account", -"label.action.enable.cluster": "Enable cluster", +"label.action.enable.cluster": "Enable Cluster", "label.action.enable.disk.offering": "Enable disk offering", "label.action.enable.maintenance.mode": "Enable maintenance mode", "label.action.enable.physical.network": "Enable physical Network", -"label.action.enable.pod": "Enable pod", +"label.action.enable.pod": "Enable Pod", "label.action.enable.role": "Enable Role", -"label.action.enable.service.offering": "Enable service offering", -"label.action.enable.system.service.offering": "Enable system service offering", +"label.action.enable.service.offering": "Enable Service Offering", +"label.action.enable.system.service.offering": "Enable System Service Offering", "label.action.enable.static.nat": "Enable static NAT", "label.action.enable.user": "Enable User", -"label.action.enable.zone": "Enable zone", +"label.action.enable.zone": "Enable Zone", "label.action.expunge.instance": "Expunge Instance", "label.action.force.reconnect": "Force reconnect", "label.action.generate.keys": "Generate keys", @@ -163,22 +170,22 @@ "label.action.iso.share": "Update ISO sharing", "label.action.lock.account": "Lock Account", "label.action.lock.user": "Lock User", -"label.action.manage.cluster": "Manage cluster", -"label.action.migrate.router": "Migrate router", +"label.action.manage.cluster": "Manage Cluster", +"label.action.migrate.router": "Migrate Router", "label.action.migrate.systemvm": "Migrate System VM", -"label.action.migrate.systemvm.to.ps": "Migrate System VM to another primary storage", -"label.action.patch.systemvm": "Patch system VM", +"label.action.migrate.systemvm.to.ps": "Migrate System VM to another Primary Storage", +"label.action.patch.systemvm": "Patch System VM", "label.action.patch.systemvm.vpc": "Patch System VM - VPC Router", "label.action.patch.systemvm.processing": "Patching System VM....", -"label.action.project.add.account": "Add Account to project", -"label.action.project.add.user": "Add User to project", +"label.action.project.add.account": "Add Account to Project", +"label.action.project.add.user": "Add User to Project", "label.action.quota.tariff.create": "Create Quota Tariff", "label.action.quota.tariff.edit": "Edit Quota Tariff", "label.action.quota.tariff.remove": "Remove Quota Tariff", "label.action.reboot.instance": "Reboot Instance", -"label.action.reboot.router": "Reboot router", +"label.action.reboot.router": "Reboot Router", "label.action.reboot.systemvm": "Reboot System VM", -"label.action.recover.volume": "Recover volume", +"label.action.recover.volume": "Recover Volume", "label.action.resize.sharedfs": "Resize Shared FileSystem", "label.action.restart.sharedfs": "Restart Shared FileSystem", "label.action.recurring.snapshot": "Recurring Snapshots", @@ -188,7 +195,7 @@ "label.action.release.asnumber": "Release AS Number", "label.action.release.ip": "Release IP", "label.action.release.reserved.ip": "Release reserved IP", -"label.action.remove.host": "Remove host", +"label.action.remove.host": "Remove Host", "label.action.remove.logical.router": "Remove Logical Router", "label.action.remove.network.policy": "Remove Network Policy", "label.action.remove.router.table.from.interface": "Remove Tungsten Fabric route table from interface", @@ -197,36 +204,41 @@ "label.action.reserve.ip": "Reserve Public IP", "label.action.reset.network.permissions": "Reset Network permissions", "label.action.reset.password": "Reset password", -"label.action.resize.volume": "Resize volume", +"label.action.resize.volume": "Resize Volume", "label.action.revert.snapshot": "Revert to Snapshot", "label.action.router.health.checks": "Get health checks result", -"label.action.run.diagnostics": "Run diagnostics", +"label.action.run.diagnostics": "Run Diagnostics", "label.action.secure.host": "Provision host security keys", "label.action.set.as.source.nat.ip": "make source NAT", "label.action.setup.2FA.user.auth": "Setup User Two Factor Authentication", "label.action.start.sharedfs": "Start Shared FileSystem", "label.action.start.instance": "Start Instance", -"label.action.start.router": "Start router", -"label.action.start.systemvm": "Start system VM", +"label.action.start.router": "Start Router", +"label.action.start.systemvm": "Start System VM", "label.action.stop.sharedfs": "Stop Shared FileSystem", "label.action.stop.instance": "Stop Instance", -"label.action.stop.router": "Stop router", -"label.action.stop.systemvm": "Stop system VM", +"label.action.stop.router": "Stop Router", +"label.action.stop.systemvm": "Stop System VM", "label.action.take.snapshot": "Take Snapshot", "label.action.template.permission": "Update Template permissions", "label.action.template.share": "Update Template sharing", -"label.action.unmanage.cluster": "Unmanage cluster", +"label.action.unmanage.cluster": "Unmanage Cluster", "label.action.unmanage.instance": "Unmanage Instance", "label.action.unmanage.instances": "Unmanage Instances", "label.action.unmanage.virtualmachine": "Unmanage Instance", +"label.action.update.cluster": "Update cluster", +"label.action.update.pod": "Update pod", +"label.action.update.zone": "Update zone", +"label.action.update.storage.pool": "Update storage pool", "label.action.unmanage.volume": "Unmanage Volume", "label.action.unmanage.volumes": "Unmanage Volumes", -"label.action.update.host": "Update host", +"label.action.unregister.extension.resource": "Unregister extension resource", +"label.action.update.host": "Update Host", "label.action.update.security.groups": "Update security groups", "label.action.update.offering.access": "Update offering access", "label.action.update.resource.count": "Update resource count", +"label.action.user.data.reset": "Reset User Data", "label.action.value": "Action/Value", -"label.action.userdata.reset": "Reset Userdata", "label.action.vmsnapshot.create": "Take Instance Snapshot", "label.action.vmsnapshot.delete": "Delete Instance Snapshot", "label.action.vmsnapshot.revert": "Revert to Instance Snapshot", @@ -237,73 +249,78 @@ "label.activeviewersessions": "Active sessions", "label.add": "Add", "label.add.account": "Add Account", +"label.add.acl.rule": "Add ACL rule", "label.add.acl": "Add ACL", -"label.add.acl.list": "Add ACL list", -"label.add.affinity.group": "Add new affinity group", +"label.add.affinity.group": "Add new Affinity Group", "label.add.baremetal.dhcp.device": "Add bare metal DHCP device", -"label.add.bgp.peer": "Add BGP peer", -"label.add.bigswitchbcf.device": "Add BigSwitch BCF controller", +"label.add.bgp.peer": "Add BGP Peer", +"label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller", "label.add.brocadevcs.device": "Add Brocade Vcs Switch", "label.add.by": "Add by", "label.add.certificate": "Add certificate", "label.add.ciscoasa1000v": "Add CiscoASA1000v resource", -"label.add.cluster": "Add cluster", -"label.add.compute.offering": "Add compute offering", +"label.add.cluster": "Add Cluster", +"label.add.compute.offering": "Add Compute Offering", "label.add.condition": "Add condition", -"label.add.disk.offering": "Add disk offering", -"label.add.domain": "Add domain", -"label.add.egress.rule": "Add egress rule", +"label.add.custom.action": "Add Custom Action", +"label.add.disk.offering": "Add Disk Offering", +"label.add.domain": "Add Domain", +"label.add.egress.rule": "Add Egress Rule", +"label.add.external.details": "Add external details", "label.add.f5.device": "Add F5 device", -"label.add.firewall": "Add firewall rule", +"label.add.firewall": "Add Firewall Rule", "label.add.firewallrule": "Add Firewall Rule", -"label.add.guest.network": "Add guest Network", -"label.add.guest.os": "Add guest os", +"label.add.gpu.card": "Add GPU card", +"label.add.gpu.device": "Add GPU device", +"label.add.guest.os.category": "Add guest OS category", +"label.add.guest.network": "Add Guest Network", +"label.add.guest.os": "Add Guest OS", "label.add.guest.os.hypervisor.mapping": "Add guest os hypervisor mapping", -"label.add.host": "Add host", -"label.add.ingress.rule": "Add ingress rule", +"label.add.host": "Add Host", +"label.add.ingress.rule": "Add Ingress Rule", "label.add.intermediate.certificate": "Add intermediate certificate", "label.add.internal.lb": "Add internal LB", -"label.add.ip.range": "Add IP range", -"label.add.ipv4.subnet": "Add IPv4 subnet for Routed networks", +"label.add.ip.range": "Add IP Range", +"label.add.ipv4.subnet": "Add IPv4 Subnet for Routed Networks", "label.add.ip.v6.prefix": "Add IPv6 prefix", -"label.add.isolated.network": "Add isolated Network", -"label.add.kubernetes.cluster": "Add Kubernetes cluster", +"label.add.isolated.network": "Add Isolated Network", +"label.add.kubernetes.cluster": "Add Kubernetes Cluster", +"label.add.acl.name": "ACL name", "label.add.ldap.account": "Add LDAP Account", -"label.add.list.name": "ACL List name", "label.add.logical.router": "Add Logical Router to this Network", "label.add.more": "Add more", -"label.add.netscaler.device": "Add Netscaler device", +"label.add.nodes": "Add Nodes to Kubernetes Cluster", +"label.add.netscaler.device": "Add Netscaler Device", "label.add.network": "Add Network", "label.add.network.acl": "Add Network ACL", -"label.add.network.acl.list": "Add Network ACL list", -"label.add.network.offering": "Add Network offering", +"label.add.network.offering": "Add Network Offering", "label.add.network.permission": "Add Network permission", -"label.add.new.gateway": "Add new gateway", +"label.add.new.gateway": "Add new Gateway", "label.add.new.tier": "Add new Network Tier", "label.add.niciranvp.device": "Add Nvp controller", "label.add.note": "Add comment", "label.add.opendaylight.device": "Add OpenDaylight controller", "label.add.pa.device": "Add Palo Alto device", "label.add.param": "Add param", -"label.add.physical.network": "Add physical Network", -"label.add.pod": "Add pod", +"label.add.physical.network": "Add Physical Network", +"label.add.pod": "Add Pod", "label.add.prefix": "Add prefix", "label.add.policy": "Add policy", -"label.add.primary.storage": "Add primary storage", -"label.add.private.gateway": "Add private gateway", -"label.add.resources": "Add resources", -"label.add.role": "Add role", -"label.add.route": "Add route", -"label.add.router.table.to.instance": "Add router table to this Instance", -"label.add.routing.policy": "Add routing policy", -"label.add.rule": "Add rule", -"label.add.secondary.ip": "Add secondary IP", -"label.add.secondary.storage": "Add secondary storage", -"label.add.security.group": "Add security group", +"label.add.primary.storage": "Add Primary Storage", +"label.add.private.gateway": "Add Private Gateway", +"label.add.resources": "Add Resources", +"label.add.role": "Add Role", +"label.add.route": "Add Route", +"label.add.router.table.to.instance": "Add Router Table to this Instance", +"label.add.routing.policy": "Add Routing Policy", +"label.add.rule": "Add Rule", +"label.add.secondary.ip": "Add Secondary IP", +"label.add.secondary.storage": "Add Secondary Storage", +"label.add.security.group": "Add Security Group", "label.add.setting": "Add setting", -"label.add.srx.device": "Add SRX device", -"label.add.static.route": "Add static route", -"label.add.system.service.offering": "Add system service offering", +"label.add.srx.device": "Add SRX Device", +"label.add.static.route": "Add Static Route", +"label.add.system.service.offering": "Add System Service Offering", "label.add.term.then": "Add term", "label.add.traffic": "Add traffic", "label.add.traffic.type": "Add traffic type", @@ -325,26 +342,27 @@ "label.add.user": "Add User", "label.add.upstream.ipv4.routes": "Add upstream IPv4 routes", "label.add.upstream.ipv6.routes": "Add upstream IPv6 routes", +"label.add.vgpu.profile": "Add profile", "label.add.vm": "Add Instance", "label.add.vms": "Add Instances", -"label.add.vmware.datacenter": "Add VMware datacenter", -"label.add.vnmc.device": "Add VNMC device", +"label.add.vmware.datacenter": "Add VMware Datacenter", +"label.add.vnmc.device": "Add VNMC Device", "label.add.vpc": "Add VPC", -"label.add.vpc.offering": "Add VPC offering", -"label.add.vpn.customer.gateway": "Add VPN customer gateway", -"label.add.vpn.gateway": "Add VPN gateway", +"label.add.vpc.offering": "Add VPC Offering", +"label.add.vpn.customer.gateway": "Add VPN Customer Gateway", +"label.add.vpn.gateway": "Add VPN Gateway", "label.add.vpn.user": "Add VPN User", -"label.add.zone": "Add zone", +"label.add.zone": "Add Zone", "label.adding": "Adding", "label.adding.user": "Adding User...", "label.address": "Address", "label.address.group": "Address group", -"label.admin": "Domain admin", +"label.admin": "Domain Admin", "label.advanced": "Advanced", "label.advanced.mode": "Advanced mode", "label.affinity": "Affinity", -"label.affinity.groups": "Affinity groups", -"label.affinitygroup": "Affinity group", +"label.affinity.groups": "Affinity Groups", +"label.affinitygroup": "Affinity Group", "label.agentcount": "Number Of connected agents", "label.agent.password": "Agent password", "label.agent.username": "Agent username", @@ -359,11 +377,12 @@ "label.all": "All", "label.all.available.data": "All available data", "label.all.ipv6": "All IPv6", -"label.all.zone": "All zones", +"label.all.zone": "All Zones", "label.allocated": "Allocated", "label.allocatedonly": "Allocated", "label.allocationstate": "Allocation state", "label.allow": "Allow", +"label.allowedroletypes": "Allowed Role Types", "label.allow.duplicate.macaddresses": "Allow duplicate MAC addresses", "label.allowuserdrivenbackups": "Allow User driven backups", "label.annotation": "Comment", @@ -428,16 +447,16 @@ "label.backup.configure.schedule": "Configure Backup Schedule", "label.backup.offering.assign": "Assign Instance to backup offering", "label.backup.offering.remove": "Remove Instance from backup offering", -"label.backup.offerings": "Backup offerings", +"label.backup.offerings": "Backup Offerings", "label.backup.repository": "Backup Repository", "label.backup.restore": "Restore Instance backup", -"label.backupofferingid": "Backup offering", -"label.backupofferingname": "Backup offering", -"label.backup.repository.add": "Add backup repository", -"label.backup.repository.remove": "Remove backup repository", "label.backuplimit": "Backup Limits", "label.backup.storage": "Backup Storage", "label.backupstoragelimit": "Backup Storage Limits (GiB)", +"label.backupofferingid": "Backup Offering ID", +"label.backupofferingname": "Backup Offering Name", +"label.backup.repository.add": "Add Backup Repository", +"label.backup.repository.remove": "Remove Backup Repository", "label.balance": "Balance", "label.bandwidth": "Bandwidth", "label.baremetal.dhcp.devices": "Bare metal DHCP devices", @@ -468,12 +487,12 @@ "label.bucket": "Bucket", "label.bucketlimit": "Bucket Limits", "label.by.account": "By Account", -"label.by.domain": "By domain", +"label.by.domain": "By Domain", "label.by.level": "By level", -"label.by.pod": "By pod", +"label.by.pod": "By Pod", "label.by.state": "By state", "label.by.type": "By type", -"label.by.zone": "By zone", +"label.by.zone": "By Zone", "label.bypassvlanoverlapcheck": "Bypass VLAN id/range overlap", "label.cachemode": "Write-cache type", "label.cancel": "Cancel", @@ -486,10 +505,13 @@ "label.category": "Category", "label.certchain": "Chain", "label.certificate": "Certificate", +"label.certificate.chain": "Certificate chain", "label.certificate.upload": "Certificate uploaded.", "label.certificate.upload.failed": "Certificate upload failed", "label.certificate.upload.failed.description": "Failed to update SSL Certificate. Failed to pass certificate validation check.", "label.certificateid": "Certificate ID", +"label.certificates": "Certificates", +"label.chainsize": "Chain size", "label.change": "Change", "label.change.affinity": "Change affinity", "label.change.bgp.peers": "Change BGP peers", @@ -510,16 +532,30 @@ "label.cisco.nexus1000v.password": "Nexus 1000v password", "label.cisco.nexus1000v.username": "Nexus 1000v username", "label.cks.cluster.autoscalingenabled": "Enable auto scaling on this cluster", +"label.cks.cluster.control.nodes.offeringid": "Service Offering for Control Nodes", +"label.cks.cluster.control.nodes.templateid": "Template for Control Nodes", +"label.cks.cluster.etcd.nodes": "Etcd Nodes", +"label.cks.cluster.etcd.nodes.offeringid": "Service Offering for etcd Nodes", +"label.cks.cluster.etcd.nodes.templateid": "Template for etcd Nodes", "label.cks.cluster.maxsize": "Maximum cluster size (Worker nodes)", "label.cks.cluster.minsize": "Minimum cluster size (Worker nodes)", +"label.cks.cluster.node.manual.upgrade": "Mark nodes for manual upgrade", "label.cks.cluster.size": "Cluster size (Worker nodes)", +"label.cks.cluster.worker.nodes.offeringid": "Service Offering for Worker Nodes", +"label.cks.cluster.worker.nodes.templateid": "Template for Worker Nodes", "label.cleanup": "Clean up", "label.clear": "Clear", +"label.clear.all": "Clear all", "label.clear.list": "Clear list", "label.clear.notification": "Clear notification", "label.clientid": "Provider Client ID", "label.close": "Close", "label.cloud.managed": "CloudManaged", +"label.cloudian.admin.password": "Admin Service Password", +"label.cloudian.admin.url": "Admin Service Endpoint URL", +"label.cloudian.admin.username": "Admin Service Username", +"label.cloudian.iam.url": "IAM Service Endpoint URL", +"label.cloudian.s3.url": "S3 Service Endpoint URL", "label.cloudian.storage": "Cloudian storage", "label.cluster": "Cluster", "label.cluster.name": "Cluster name", @@ -539,14 +575,17 @@ "label.communities": "Communities", "label.community": "Community", "label.complete": "Complete", +"label.completed": "Completed", "label.compute": "Compute", -"label.compute.offerings": "Compute offerings", -"label.compute.offering.for.sharedfs.instance": "Compute offering for Instance", +"label.compute.offerings": "Compute Offerings", +"label.compute.offering.for.sharedfs.instance": "Compute Offering for Instance", "label.computeonly.offering": "Compute only disk offering", "label.computeonly.offering.tooltip": "Option to specify root disk related information in the compute offering or to directly link a disk offering to the compute offering", "label.conditions": "Conditions", "label.configuration": "Configuration", +"label.configuration.details": "Configuration Details", "label.configure": "Configure", + "label.configure.instance": "Configure Instance", "label.configure.health.monitor": "Configure Health Monitor", "label.configure.app": "Configure the App", "label.configure.ldap": "Configure LDAP", @@ -559,7 +598,7 @@ "label.confirm.delete.loadbalancer.rules": "Please confirm you wish to delete the selected load balancing rules.", "label.confirm.delete.portforward.rules": "Please confirm you wish to delete the selected port-forward rules.", "label.confirm.delete.routing.firewall.rules": "Please confirm you wish to delete the selected IPv4 Routing firewall rules", -"label.confirm.delete.snapshot.zones": "Please confirm you wish to delete the Snapshot in the selected zones.", +"label.confirm.delete.snapshot.zones": "Please confirm you wish to delete the Snapshot in the selected Zones.", "label.confirm.delete.templates": "Please confirm you wish to delete the selected Templates.", "label.confirm.delete.tungsten.address.group": "Please confirm that you would like to delete this Address Group", "label.confirm.delete.tungsten.firewall.policy": "Please confirm that you would like to delete this Firewall Policy", @@ -595,11 +634,12 @@ "label.copyid": "Copy ID", "label.copy.password": "Copy password", "label.core": "Core", -"label.core.zone.type": "Core zone type", +"label.core.zone.type": "Core Zone type", +"label.count": "Count", "label.counter": "Counter", "label.counter.name": "Name of the counter for which the policy will be evaluated", "label.cpu": "CPU", -"label.cpu.sockets": "CPU sockets", +"label.cpu.sockets": "CPU Sockets", "label.cpu.usage.info": "CPU usage information", "label.cpuallocated": "CPU allocated for Instances", "label.cpuallocatedghz": "CPU allocated", @@ -614,26 +654,27 @@ "label.cpuused": "CPU utilized", "label.cpuusedghz": "CPU used", "label.create": "Create", -"label.create.instance": "Create cloud server", +"label.create.instance": "Create Cloud Server", "label.create.account": "Create Account", "label.create.asnrange": "Create AS Range", -"label.create.backup": "Start backup", +"label.create.backup": "Start Backup", +"label.create.extension": "Create Extension", "label.create.sharedfs": "Create Shared FileSystem", "label.create.network": "Create new Network", "label.create.nfs.secondary.staging.storage": "Create NFS secondary staging storage", -"label.create.project": "Create project", -"label.create.project.role": "Create project role", +"label.create.project": "Create Project", +"label.create.project.role": "Create Project Role", "label.create.routing.policy": "Create Routing Policy", "label.create.site.vpn.connection": "Create site-to-site VPN connection", "label.create.site.vpn.gateway": "Create site-to-site VPN gateway", -"label.create.snapshot.for.volume": "Created Snapshot for volume", +"label.create.snapshot.for.volume": "Created Snapshot for Volume", "label.create.ssh.key.pair": "Create a SSH Key Pair", "label.create.template": "Create Template", "label.create.tier.aclid.description": "The ACL associated with the Network Tier.", "label.create.tier.externalid.description": "ID of the Network in an external system.", -"label.create.tier.gateway.description": "The Network Tier's gateway in the super CIDR range, not overlapping with the CIDR of other Network Tiers in this VPC.", +"label.create.tier.gateway.description": "Gateway IP must be within VPC CIDR ({value})", "label.create.tier.name.description": "A unique name for the Network Tier.", -"label.create.tier.netmask.description": "The Network Tier's netmask. For example 255.255.255.0", +"label.create.tier.netmask.description": "Network Tier's netmask must be more restrictive than {value}", "label.create.tier.networkofferingid.description": "The Network offering for the Network Tier.", "label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy", "label.create.user": "Create User", @@ -647,20 +688,25 @@ "label.credit": "Credit", "label.cron": "Cron expression", "label.cron.mode": "Cron mode", -"label.crosszones": "Cross zones", +"label.crosszones": "Cross Zones", "label.currency": "Currency", "label.current": "Current", "label.current.storage": "Current storage", "label.currentpassword": "Current password", "label.custom": "Custom", +"label.customactionid": "Custom Action", +"label.customactions": "Custom Actions", +"label.custom.actions": "Custom Actions", "label.customconstrained": "Custom constrained", "label.customdisksize": "Custom disk size", "label.customunconstrained": "Custom unconstrained", "label.daily": "Daily", "label.dark.mode": "Dark mode", "label.dashboard": "Dashboard", -"label.data.disk": "Data disk", -"label.data.disk.offering": "Data disk offering", +"label.data.disk": "Data Disk", +"label.data.disk.offering": "Data Disk Offering", +"label.data.pool": "Data pool", +"label.data.pool.description": "Data pool is required when using a Ceph pool with erasure code", "label.date": "Date", "label.datetime.filter.period": "From {startDate} to {endDate}", "label.datetime.filter.starting": "Starting {startDate}.", @@ -669,19 +715,19 @@ "label.days": "Days", "label.day.of.month": "Day of month", "label.day.of.week": "Day of week", -"label.db.usage.metrics": "DB/Usage server", +"label.db.usage.metrics": "DB/Usage Server", "label.dbislocal": "The db runs locally", "label.dc.name": "DC name", "label.declare.host.as.degraded": "Declare host as degraded", "label.decline.invitation": "Decline invitation", "label.dedicate": "Dedicate", "label.dedicate.bgp.peer": "Dedicate BGP peer", -"label.dedicate.cluster": "Dedicate cluster", -"label.dedicate.host": "Dedicate host", +"label.dedicate.cluster": "Dedicate Cluster", +"label.dedicate.host": "Dedicate Host", "label.dedicate.ipv4.subnet": "Dedicate IPv4 subnet", -"label.dedicate.pod": "Dedicate pod", +"label.dedicate.pod": "Dedicate Pod", "label.dedicate.vlan.vni.range": "Dedicate VLAN/VNI range", -"label.dedicate.zone": "Dedicate zone", +"label.dedicate.zone": "Dedicate Zone", "label.dedicated": "Dedicated", "label.dedicated.vlan.vni.ranges": "Dedicated VLAN/VNI ranges", "label.dedicatedresources": "Dedicated resources", @@ -692,7 +738,7 @@ "label.default.network.guestcidraddress.isolated.network": "Default guest CIDR for Isolated Networks", "label.defaultnetwork": "Default Network", "label.delete": "Delete", -"label.delete.acl.list": "Delete ACL list", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "Delete affinity group", "label.delete.alerts": "Delete alerts", "label.delete.asnrange": "Delete AS Range", @@ -705,11 +751,12 @@ "label.delete.ciscoasa1000v": "Delete CiscoASA1000v", "label.delete.ciscovnmc.resource": "Delete CiscoVNMC resource", "label.delete.condition": "Delete condition", +"label.delete.custom.action": "Delete Custom Action", "label.delete.dedicated.vlan.range": "Deleted dedicated VLAN/VNI range.", "label.delete.domain": "Delete domain", "label.delete.events": "Delete events", +"label.delete.extension": "Delete Extension", "label.delete.f5": "Delete F5", -"label.destroy.sharedfs": "Destroy Shared FileSystem", "label.delete.gateway": "Delete gateway", "label.delete.icon": "Delete icon", "label.delete.instance.group": "Delete Instance group", @@ -723,13 +770,13 @@ "label.delete.portforward.rules": "Delete port forward rules", "label.delete.project": "Delete project", "label.delete.project.role": "Delete project role", -"label.delete.role": "Delete role", -"label.delete.rule": "Delete rule", -"label.delete.setting": "Delete setting", +"label.delete.role": "Delete Role", +"label.delete.rule": "Delete Rule", +"label.delete.setting": "Delete Setting", "label.delete.snapshot.policy": "Delete Snapshot policy", "label.delete.srx": "Delete SRX", "label.delete.sslcertificate": "Delete SSL certificate", -"label.delete.tag": "Remove tag", +"label.delete.tag": "Remove Tag", "label.delete.term": "Delete term", "label.delete.traffic.type": "Delete traffic type", "label.delete.tungsten.address.group": "Delete Address Group", @@ -759,13 +806,13 @@ "label.deployasis": "Read Instance settings from OVA", "label.deploymentplanner": "Deployment planner", "label.desc.db.stats": "Database Statistics", -"label.desc.importexportinstancewizard": "Import and export Instances to/from an existing VMware or KVM cluster.", +"label.desc.importexportinstancewizard": "Import and export Instances to/from an existing VMware or KVM Cluster.", "label.desc.import.ext.kvm.wizard": "Import Instance from remote KVM host", "label.desc.import.local.kvm.wizard": "Import QCOW2 image from Local Storage", "label.desc.import.shared.kvm.wizard": "Import QCOW2 image from Shared Storage", "label.desc.import.unmanage.volume": "Import and unmanage volume on Storage Pools", "label.desc.ingesttinstancewizard": "Ingest instances from an external KVM host", -"label.desc.importmigratefromvmwarewizard": "Import instances from VMware into a KVM cluster", +"label.desc.importmigratefromvmwarewizard": "Import instances from VMware into a KVM Cluster", "label.desc.usage.stats": "Usage Server Statistics", "label.description": "Description", "label.destaddressgroupuuid": "Destination Address Group", @@ -788,10 +835,12 @@ "label.destroying": "Destroying", "label.destroyed": "Destroyed", "label.destroy.router": "Destroy router", +"label.destroy.sharedfs": "Destroy Shared FileSystem", "label.deststartport": "Destination Start Port", "label.desttaguuid": "Destination Tag", "label.details": "Details", "label.deviceid": "Device ID", +"label.devicename": "Device Name", "label.devices": "Devices", "label.dhcp": "DHCP", "label.direct.attached.public.ip": "Direct attached public IP", @@ -799,17 +848,20 @@ "label.directdownload": "Direct download", "label.direction": "Direction", "label.disable.autoscale.vmgroup": "Disable AutoScaling Group", +"label.disable.custom.action": "Disable Custom Action", +"label.disable.extension": "Disable Extension", "label.disable.host": "Disable host", -"label.disable.network.offering": "Disable Network offering", +"label.disable.network.offering": "Disable Network Offering", "label.disable.provider": "Disable provider", "label.disable.storage": "Disable storage pool", -"label.disable.vpc.offering": "Disable VPC offering", +"label.disable.vpc.offering": "Disable VPC Offering", "label.disable.vpn": "Disable remote access VPN", "label.disable.webhook": "Disable Webhook", "label.disabled": "Disabled", "label.disconnected": "Last disconnected", +"label.discover.gpu.devices": "Discover GPU devices", "label.disk": "Disk", -"label.disk.offerings": "Disk offerings", +"label.disk.offerings": "Disk Offerings", "label.disk.path": "Disk Path", "label.disk.tooltip": "Disk Image filename in the selected Storage Pool", "label.disk.selection": "Disk selection", @@ -830,9 +882,9 @@ "label.diskkbswrite": "Disk write (KiB)", "label.diskread": "Disk read", "label.diskwrite": "Disk write", -"label.diskoffering": "Disk offering", -"label.diskofferingdisplaytext": "Disk offering", -"label.diskofferingid": "Disk offering", +"label.diskoffering": "Disk Offering", +"label.diskofferingdisplaytext": "Disk Offering", +"label.diskofferingid": "Disk Offering", "label.disksize": "Disk size (in GB)", "label.disksizeallocated": "Disk allocated", "label.disksizeallocatedgb": "Allocated", @@ -853,7 +905,7 @@ "label.domain": "Domain", "label.domain.id": "Domain ID", "label.domain.name": "Domain name", -"label.domain.router": "Domain router", +"label.domain.router": "Domain Router", "label.domain.suffix": "DNS domain suffix (i.e., xyz.com)", "label.domainid": "Domain", "label.domainname": "Domain", @@ -893,15 +945,15 @@ "label.edge.zone": "Edge Zone", "label.edit": "Edit", "label.edit.account": "Edit Account", -"label.edit.acl.list": "Edit ACL list", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.autoscale.vmprofile": "Edit AutoScale Instance Profile", "label.edit.project.details": "Edit project details", "label.edit.project.role": "Edit project role", -"label.edit.role": "Edit role", -"label.edit.rule": "Edit rule", +"label.edit.role": "Edit Role", +"label.edit.rule": "Edit Rule", "label.edit.secondary.ips": "Edit secondary IPs", -"label.edit.tags": "Edit tags", +"label.edit.tags": "Edit Tags", "label.edit.traffic.type": "Edit traffic type", "label.edit.user": "Edit User", "label.egress": "Egress", @@ -911,15 +963,18 @@ "label.elastic": "Elastic", "label.email": "Email", "label.enable.autoscale.vmgroup": "Enable AutoScaling Group", +"label.enable.custom.action": "Enable Custom Action", +"label.enable.extension": "Enable Extension", "label.enable.host": "Enable Host", -"label.enable.network.offering": "Enable Network offering", +"label.enable.network.offering": "Enable Network Offering", "label.enable.oauth": "Enable OAuth Login", "label.enable.provider": "Enable provider", -"label.enable.storage": "Enable storage pool", -"label.enable.vpc.offering": "Enable VPC offering", +"label.enable.storage": "Enable Storage Pool", +"label.enable.vpc.offering": "Enable VPC Offering", "label.enable.vpn": "Enable remote access VPN", "label.enable.webhook": "Enable Webhook", "label.enabled": "Enabled", +"label.enabled.revocation.check": "Enables revocation checking for certificates", "label.encrypt": "Encrypt", "label.encryptroot": "Encrypt Root Disk", "label.end": "End", @@ -935,12 +990,14 @@ "label.endipv6": "IPv6 end IP", "label.endpoint": "Endpoint", "label.endport": "End port", +"label.enter.account.name": "Enter the account name", "label.enter.code": "Enter 2FA code to verify", "label.enter.static.pin": "Enter static PIN to verify", "label.enter.token": "Enter token", "label.entityid": "Entity", "label.entitytype": "Entity Type", "label.error": "Error", +"label.errormessage": "Error message", "label.error.caught": "Error caught", "label.error.code": "Error code", "label.error.file.read": "Cannot read file.", @@ -968,14 +1025,22 @@ "label.existing": "Existing", "label.execute": "Execute", "label.expunge": "Expunge", - "label.expunge.sharedfs": "Expunge Shared FileSystem", +"label.expunge.sharedfs": "Expunge Shared FileSystem", "label.expungevmgraceperiod": "Expunge Instance grace period (in sec)", "label.expunged": "Expunged", "label.expunging": "Expunging", "label.export.rules": "Export Rules", "label.ext.hostname.tooltip": "External Host Name or IP Address", -"label.external.managed": "ExternalManaged", +"label.extension": "Extension", +"label.extensions": "Extensions", +"label.extensionid": "Extension", +"label.extensionname": "Extension", "label.external": "External", +"label.external.managed": "ExternalManaged", +"label.external.details": "External provisioning details", +"label.externaldetails": "External details", +"label.external.details.tooltip": "Details that will be passed to the external provisioner while deploying an instance", +"label.externalprovisioner": "External provisioner", "label.external.link": "External link", "label.externalid": "External Id", "label.externalloadbalanceripaddress": "External load balancer IP address.", @@ -984,6 +1049,7 @@ "label.f5.ip.loadbalancer": "F5 BIG-IP load balancer.", "label.failed": "Failed", "label.featured": "Featured", +"label.fetch.from.backup": "Fetch from Backup", "label.fetch.instances": "Fetch Instances", "label.fetch.latest": "Fetch latest", "label.filename": "File Name", @@ -996,20 +1062,23 @@ "label.filter.annotations.self": "Created by me", "label.filterby": "Filter by", "label.fingerprint": "FingerPrint", +"label.skip": "Skip", "label.finish": "Finish", "label.firewall": "Firewall", "label.firewall.policy": "Firewall Policy", "label.firewallpolicy": "Firewall Policy", -"label.firewallrule": "Firewall rule", +"label.firewallrule": "Firewall Rule", "label.firewallruleuuid": "Firewall Rule", "label.firstname": "First name", "label.firstname.lower": "firstname", "label.fix.errors": "Fix errors", -"label.fixed": "Fixed offering", +"label.fixed": "Fixed Offering", "label.for": "for", +"label.forcks": "For CKS", "label.forbidden": "Forbidden", "label.forced": "Force", -"label.force.ms.to.import.vm.files": "Force MS to export OVF from VMware to temporary storage", +"label.force.ms.to.import.vm.files": "Enable to force OVF Download via Management Server. Disable to use KVM Host ovftool (if installed)", +"label.force.update.os.type": "Force update OS type", "label.force.stop": "Force stop", "label.force.reboot": "Force reboot", "label.forceencap": "Force UDP encapsulation of ESP packets", @@ -1036,6 +1105,26 @@ "label.glustervolume": "Volume", "label.go.back": "Go back", "label.gpu": "GPU", +"label.gpucardid": "GPU Card", +"label.gpucardname": "GPU Card", +"label.gpu.card": "GPU Card", +"label.gpu.card.types": "GPU Card Types", +"label.gpu.count": "GPU Count", +"label.gpucount": "GPU Count", +"label.gpu.device": "GPU Device", +"label.gpu.devices": "GPU Devices", +"label.gpu.enabled": "GPU Enabled", +"label.gpuenabled": "GPU Enabled", +"label.gpudevicetype": "Device Type", +"label.gpu.devices.add": "Add GPU Device", +"label.gpu.devices.delete": "Delete GPU Device", +"label.gpu.devices.manage": "Manage GPU Device", +"label.gpu.devices.unmanage": "Unmanage GPU Device", +"label.gpu.display": "GPU Display", +"label.gpulimit": "GPU limits", +"label.gpu.summary": "Summary", +"label.gputotal": "GPU Total", +"label.gpuused": "GPU Used", "label.chart.info": "Information about the charts", "label.group": "Group", "label.group.optional": "Group (Optional)", @@ -1055,6 +1144,8 @@ "label.guest.netmask": "Guest netmask", "label.guest.networks": "Guest Networks", "label.guest.os": "Guest OS", +"label.guest.os.category": "Guest OS Category", +"label.guest.os.categories": "Guest OS Categories", "label.guest.os.hypervisor.mappings": "Guest OS mappings", "label.guest.start.ip": "Guest start IP", "label.guest.traffic": "Guest traffic", @@ -1093,7 +1184,9 @@ "label.host": "IP address", "label.host.alerts": "Hosts in alert state", "label.host.name": "Host name", +"label.host.ovftool.version": "OVFTool Version", "label.host.tag": "Host tag", +"label.host.virtv2v.version": "Virt-v2v Version", "label.hostcontrolstate": "Compute Resource Status", "label.hostid": "Host", "label.hostname": "Host", @@ -1109,7 +1202,7 @@ "label.hosttags.implicit.description": "The host tags defined by CloudStack Agent", "label.hourly": "Hourly", "label.hypervisor": "Hypervisor", -"label.hypervisor.capabilities": "Hypervisor capabilities", +"label.hypervisor.capabilities": "Hypervisor Capabilities", "label.hypervisor.type": "Hypervisor type", "label.hypervisors": "Hypervisors", "label.hypervisorsnapshotreserve": "Hypervisor Snapshot reserve", @@ -1129,14 +1222,17 @@ "label.ikelifetime": "IKE lifetime (second)", "label.ikepolicy": "IKE policy", "label.ikeversion": "IKE version", +"label.image": "Image", +"label.image.type": "Image type", "label.images": "Images", "label.imagestoreid": "Secondary Storage", -"label.import.backup.offering": "Import backup offering", +"label.import.backup.offering": "Import Backup Offering", "label.import.instance": "Import Instance", -"label.import.offering": "Import offering", -"label.import.role": "Import role", +"label.import.offering": "Import Offering", +"label.import.role": "Import Role", "label.import.volume": "Import Volume", "label.inactive": "Inactive", +"label.inbuilt": "Inbuilt", "label.in.progress": "in progress", "label.in.progress.for": "in progress for", "label.info": "Info", @@ -1148,16 +1244,18 @@ "label.initial": "Inital", "label.initialized": "Initalized", "label.insideportprofile": "Inside port profile", -"label.installwizard.addzoneintro.title": "Let's add a zone", +"label.installwizard.addzoneintro.title": "Let's add a Zone", "label.installwizard.subtitle": "This guide will aid you in setting up your CloudStack™ installation", "label.installwizard.title": "Hello and welcome to CloudStack™", "label.instance": "Instance", "label.instance.conversion.support": "Instance Conversion Supported", -"label.instance.groups": "Instance groups", +"label.instance.groups": "Instance Groups", +"label.instance.metadata": "Instance metadata", "label.instance.name": "Instance name", "label.instancename": "Internal name", "label.instanceport": "Instance port", "label.instances": "Instances", +"label.leasedinstances": "Leased Instances", "label.interface.route.table": "Interface Route Table", "label.interface.router.table": "Interface Router Table", "label.intermediate.certificate": "Intermediate certificate", @@ -1275,16 +1373,18 @@ "label.keyboardtype": "Keyboard type", "label.keypair": "SSH key pair", "label.keypairs": "SSH key pair(s)", -"label.kubeconfig.cluster": "Kubernetes cluster config", +"label.kubeconfig.cluster": "Kubernetes Cluster config", "label.kubernetes": "Kubernetes", -"label.kubernetes.access.details": "The kubernetes nodes can be accessed via ssh using:
ssh -i [ssh_key] -p [port_number] cloud@[public_ip_address]

where,
ssh_key: points to the ssh private key file corresponding to the key that was associated while creating the Kubernetes cluster. If no ssh key was provided during Kubernetes cluster creation, use the ssh private key of the management server.
port_number: can be obtained from the Port Forwarding Tab (Public Port column)", -"label.kubernetes.cluster": "Kubernetes cluster", -"label.kubernetes.cluster.create": "Create Kubernetes cluster", -"label.kubernetes.cluster.delete": "Delete Kubernetes cluster", -"label.kubernetes.cluster.scale": "Scale Kubernetes cluster", -"label.kubernetes.cluster.start": "Start Kubernetes cluster", -"label.kubernetes.cluster.stop": "Stop Kubernetes cluster", -"label.kubernetes.cluster.upgrade": "Upgrade Kubernetes cluster", +"label.kubernetes.access.details": "The kubernetes nodes can be accessed via ssh using:
ssh -i [ssh_key] -p [port_number] cloud@[public_ip_address]

where,
ssh_key: points to the ssh private key file corresponding to the key that was associated while creating the Kubernetes Cluster. If no ssh key was provided during Kubernetes cluster creation, use the ssh private key of the management server.
port_number: can be obtained from the Port Forwarding Tab (Public Port column)", +"label.kubernetes.cluster.add.nodes.to.cluster": "Add nodes to Kubernetes cluster", +"label.kubernetes.cluster.remove.nodes.from.cluster": "Remove nodes from Kubernetes cluster", +"label.kubernetes.cluster": "Kubernetes Cluster", +"label.kubernetes.cluster.create": "Create Kubernetes Cluster", +"label.kubernetes.cluster.delete": "Delete Kubernetes Cluster", +"label.kubernetes.cluster.scale": "Scale Kubernetes Cluster", +"label.kubernetes.cluster.start": "Start Kubernetes Cluster", +"label.kubernetes.cluster.stop": "Stop Kubernetes Cluster", +"label.kubernetes.cluster.upgrade": "Upgrade Kubernetes Cluster", "label.kubernetes.dashboard": "Kubernetes dashboard UI", "label.kubernetes.dashboard.create.token": "Create token for Kubernetes dashboard", "label.kubernetes.dashboard.create.token.desc": "Since Kubernetes v1.24.0, there is no auto-generation of secret-based service Account token due to security reason. You need to create a service Account and an optional long-lived Bearer Token for the service Account.", @@ -1314,9 +1414,9 @@ "label.launch": "Launch", "label.launch.vm": "Launch Instance", "label.launch.vm.and.stay": "Launch Instance & stay on this page", -"label.launch.vnf.appliance": "Launch VNF appliance", -"label.launch.vnf.appliance.and.stay": "Launch VNF appliance & stay on this page", -"label.launch.zone": "Launch zone", +"label.launch.vnf.appliance": "Launch VNF Appliance", +"label.launch.vnf.appliance.and.stay": "Launch VNF Appliance & stay on this page", +"label.launch.zone": "Launch Zone", "label.lb.algorithm.leastconn": "Least connections", "label.lb.algorithm.roundrobin": "Round-robin", "label.lb.algorithm.source": "Source", @@ -1330,8 +1430,8 @@ "label.lbruleid": "Load balancer ID", "label.lbtype": "Load balancer type", "label.ldap": "LDAP", -"label.ldap.configuration": "LDAP configuration", -"label.ldap.group.name": "LDAP group", +"label.ldap.configuration": "LDAP Configuration", +"label.ldap.group.name": "LDAP Group", "label.level": "Level", "label.license.agreements": "License agreements", "label.limit": "Limit", @@ -1345,11 +1445,11 @@ "label.list.ciscoasa1000v": "ASA 1000v", "label.list.ciscovnmc": "Cisco VNMC", "label.list.nodes": "List nodes", -"label.list.pods": "List pods", +"label.list.pods": "List Pods", "label.list.services": "List services", "label.list.vmware.vcenter.vms": "List VMware Instances", "label.livepatch": "Live patch Network's router(s)", -"label.load.balancer": "Load balancer", +"label.load.balancer": "Load Balancer", "label.loadbalancerinstance": "Assigned Instances", "label.loadbalancerrule": "Load balancing rule", "label.loadbalancing": "Load balancing", @@ -1383,6 +1483,7 @@ "label.make.user.project.owner": "Make User project owner", "label.makeredundant": "Make redundant", "label.manage": "Manage", +"label.manage.ssl.cert": "Manage SSL certificate", "label.manage.vpn.user": "Manage VPN Users", "label.managed.instances": "Managed Instances", "label.managed.volumes": "Managed Volumes", @@ -1391,10 +1492,9 @@ "label.managementserverid": "Management server", "label.managementservername": "Management server", "label.management.ips": "Management IP addresses", -"label.management.server": "Management server", -"label.management.servers": "Management servers", +"label.management.server": "Management Server", +"label.management.servers": "Management Servers", "label.management.server.peers": "Peers", -"label.managementservername": "Management Server", "label.managementservers": "Number of management servers", "label.matchall": "Match all", "label.max": "Max.", @@ -1409,8 +1509,10 @@ "label.maxcpunumber": "Max CPU cores", "label.maxdatavolumeslimit": "Max data volumes limit", "label.maxerrorretry": "Max error retry", +"label.maxgpu": "Max. GPUs", "label.maxguestslimit": "Max guest limit", -"label.maxhostspercluster": "Max hosts per cluster", +"label.maxheads": "Max. heads", +"label.maxhostspercluster": "Max hosts per Cluster", "label.maximum": "Maximum", "label.maxinstance": "Max Instances", "label.maxiops": "Max IOPS", @@ -1419,14 +1521,17 @@ "label.maxnetwork": "Max. Networks", "label.maxobjectstorage": "Max. Object Storage (GiB)", "label.maxprimarystorage": "Max. primary storage (GiB)", -"label.maxproject": "Max. projects", +"label.maxproject": "Max. Projects", "label.maxpublicip": "Max. public IPs", +"label.maxresolutionx": "Max. resolution X", +"label.maxresolutiony": "Max. resolution Y", "label.maxsecondarystorage": "Max. secondary storage (GiB)", "label.maxsize": "Maximum size", "label.maxsnapshot": "Max. Snapshots", "label.maxtemplate": "Max. Templates", "label.maxuservm": "Max. User Instances", -"label.maxvolume": "Max. volumes", +"label.maxvgpuperphysicalgpu": "Max. vGPUs per physical GPU", +"label.maxvolume": "Max. Volumes", "label.maxvpc": "Max. VPCs", "label.may.continue": "You may now continue.", "label.mb.memory": "MB memory", @@ -1446,8 +1551,8 @@ "label.memoryused": "Used memory", "label.memoryusedgb": "Memory used", "label.memused": "Memory usage", -"label.menu.security.groups": "Security groups", -"label.menu.service.offerings": "Service offerings", +"label.menu.security.groups": "Security Groups", +"label.menu.service.offerings": "Service Offerings", "label.metadata": "Metadata", "label.metadata.description": "Metadata of the Object", "label.metadata.upload.description": "Set metadata for the object", @@ -1459,8 +1564,8 @@ "label.migrate.instance.to.ps": "Migrate Instance to another primary storage", "label.migrate.instance.single.storage": "Migrate all volume(s) of the Instance to a single primary storage", "label.migrate.instance.specific.storages": "Migrate volume(s) of the Instance to specific primary storages", -"label.migrate.systemvm.to": "Migrate system VM to", -"label.migrate.volume": "Migrate volume", +"label.migrate.systemvm.to": "Migrate System VM to", +"label.migrate.volume": "Migrate Volume", "message.memory.usage.info.hypervisor.additionals": "The data shown may not reflect the actual memory usage if the Instance does not have the additional hypervisor tools installed", "message.memory.usage.info.negative.value": "If the Instance's memory usage cannot be obtained from the hypervisor, the lines for free memory in the raw data graph and memory usage in the percentage graph will be disabled", "message.migrate.volume.tooltip": "Volume can be migrated to any suitable storage pool. Admin has to choose the appropriate disk offering to replace, that supports the new storage pool", @@ -1492,7 +1597,9 @@ "label.monitor.url": "URL Path", "label.monthly": "Monthly", "label.more.access.dashboard.ui": "More about accessing dashboard UI", +"label.mount.cks.iso.on.vr": "Use CKS packages from Virtual Router", "label.mount.sharedfs": "Mount Shared FileSystem via NFS", +"label.mountopts": "Mount options", "label.move.down.row": "Move down one row", "label.move.to.bottom": "Move to bottom", "label.move.to.top": "Move to top", @@ -1506,20 +1613,31 @@ "label.native": "Native", "label.ncc": "NCC", "label.netmask": "Netmask", +"label.netris": "Netris", +"label.netristag": "Netris tag", +"label.netrisurl": "Netris URL", +"label.netris.provider": "Netris Provider", +"label.netris.provider.name": "Netris provider name", +"label.netris.provider.username": "Netris provider username", +"label.netris.provider.password": "Netris provider password", +"label.netris.provider.site": "Netris provider Site name", +"label.netris.provider.tenant.name": "Netris provider Admin Tenant name", +"label.netris.provider.tag": "Netris Tag", +"label.netris.provider.url": "Netris provider URL", "label.netscaler": "NetScaler", "label.netscaler.mpx": "NetScaler MPX LoadBalancer", "label.netscaler.sdx": "NetScaler SDX LoadBalancer", "label.netscaler.vpx": "NetScaler VPX LoadBalancer", "label.network": "Network", "label.network.acl": "Network ACL", -"label.network.acl.lists": "Network ACL lists", +"label.network.acls": "Network ACLs", "label.network.addvm": "Add Network to Instance", "label.network.desc": "Network desc", -"label.network.domain": "Network domain", +"label.network.domain": "Network Domain", "label.network.label.display.for.blank.value": "Use default gateway", "label.network.name": "Network name", -"label.network.offering": "Network offering", -"label.network.offerings": "Network offerings", +"label.network.offering": "Network Offerings", +"label.network.offerings": "Network Offerings", "label.network.policy": "Network Policy", "label.network.restart.required": "Network restart required", "label.network.route.table": "Network route table", @@ -1536,9 +1654,9 @@ "label.networklimit": "Network limits", "label.networkmode": "Network Mode", "label.networkname": "Network name", -"label.networkofferingdisplaytext": "Network offering", -"label.networkofferingid": "Network offering", -"label.networkofferingname": "Network offering", +"label.networkofferingdisplaytext": "Network Offering", +"label.networkofferingid": "Network Offering", +"label.networkofferingname": "Network Offering", "label.networkrate": "Network rate (Mb/s)", "label.networkread": "Network read", "label.networks": "Networks", @@ -1548,9 +1666,9 @@ "label.never": "Never", "label.new": "New", "label.new.autoscale.vmgroup": "New AutoScaling Group", -"label.new.instance.group": "New Instance group", +"label.new.instance.group": "New Instance Group", "label.new.password": "New password", -"label.new.project": "New project", +"label.new.project": "New Project", "label.new.secondaryip.description": "Enter new secondary IP address", "label.new.tag": "New tag", "label.new.vm": "New Instance", @@ -1559,6 +1677,7 @@ "label.newinstance": "New Instance", "label.newname": "New name", "label.next": "Next", +"label.nexthop": "Next hop", "label.nfs": "NFS", "label.nfsmountopts": "NFS mount options", "label.nfsserver": "NFS server", @@ -1575,6 +1694,7 @@ "label.no.items": "No available Items", "label.no.matching.offering": "No matching offering found", "label.no.matching.network": "No matching Networks found", +"label.node.version": "Node version", "label.no.usage.records": "No usage records found", "label.noderootdisksize": "Node root disk size (in GB)", "label.nodiskcache": "No disk cache", @@ -1591,16 +1711,17 @@ "label.nsx.provider.port": "NSX provider port", "label.nsx.provider.username": "NSX provider username", "label.nsx.provider.password": "NSX provider password", -"label.nsx.provider.edgecluster": "NSX provider edge cluster", +"label.nsx.provider.edgecluster": "NSX provider edge Cluster", "label.nsx.provider.tier0gateway": "NSX provider tier-0 gateway", -"label.nsx.provider.transportzone": "NSX provider transport zone", +"label.nsx.provider.transportzone": "NSX provider transport Zone", "label.nsx.supports.internal.lb": "Enable NSX internal LB service", "label.nsx.supports.lb": "Enable NSX LB service", "label.num.cpu.cores": "# of CPU cores", +"label.numanode": "NUMA node", "label.number": "#Rule", "label.numretries": "Number of retries", "label.nvpdeviceid": "ID", -"label.oauth.configuration": "OAuth configuration", +"label.oauth.configuration": "OAuth Configuration", "label.oauth.verification": "OAuth verification", "label.ocfs2": "OCFS2", "label.object.storage" : "Object Storage", @@ -1618,7 +1739,7 @@ "label.of.month": "of month", "label.offerha": "Offer HA", "label.offeringid": "Offering ID", -"label.offeringtype": "Compute offering type", +"label.offeringtype": "Compute Offering type", "label.ok": "OK", "label.only.end.date.and.time": "Only end date and time", "label.only.start.date.and.time": "Only start date and time", @@ -1635,7 +1756,11 @@ "label.operator.less.or.equal": "Less than or equals to", "label.operator.equal": "Equals to", "label.optional": "Optional", +"label.options": "Options", +"label.orchestrator": "Orchestrator", +"label.orchestratorrequirespreparevm": "Requires Prepare Instance", "label.order": "Order", +"label.os": "Operating System", "label.oscategoryid": "OS category", "label.oscategoryname": "OS category name", "label.osname": "OS name", @@ -1675,9 +1800,11 @@ "label.palo.alto.firewall": "Palo Alto firewall", "label.palp": "Palo Alto log profile", "label.params": "Parameters", +"label.parameters": "Parameters", "label.param.name": "Parameter name", "label.param.value": "Parameter value", "label.parentdomainname": "Parent domain", +"label.parentgpudeviceid": "Parent GPU device", "label.parentname": "Parent", "label.parentsubnet": "Parent Subnet", "label.passive": "Passive", @@ -1687,8 +1814,10 @@ "label.password.tooltip": "The password for the Host", "label.passwordenabled": "Password enabled", "label.path": "Path", +"label.pathready": "Path Ready", +"label.pathstate": "Path State", "label.patp": "Palo Alto threat profile", -"label.pavr": "Virtual router", +"label.pavr": "Virtual Router", "label.payload": "Payload", "label.payloadurl": "Payload URL", "label.pcidevice": "GPU", @@ -1702,7 +1831,7 @@ "label.pending.jobs": "Pending Jobs", "label.pendingjobscount": "Number Of pending jobs", "label.per.account": "Per Account", -"label.per.zone": "Per zone", +"label.per.zone": "Per Zone", "label.percentage": "Percentage", "label.perfectforwardsecrecy": "Perfect forward secrecy", "label.perform.fresh.checks": "Perform fresh checks", @@ -1734,7 +1863,7 @@ "label.powerflex.gateway": "Gateway", "label.powerflex.gateway.password": "Gateway password", "label.powerflex.gateway.username": "Gateway username", -"label.powerflex.storage.pool": "Storage pool", +"label.powerflex.storage.pool": "Storage Pool", "label.powerstate": "Power state", "label.preferred": "Preferred", "label.prefix": "Prefix", @@ -1742,6 +1871,8 @@ "label.prepare.for.shutdown": "Prepare for Shutdown", "label.prepare.for.maintenance": "Prepare for Maintenance", "label.presetup": "PreSetup", +"label.press.enter": "Press enter when done.", +"label.press.enter.tooltip": "On leaving a any of these fields above, when they all contain values, or on pressing enter, ESXi hosts will be retrieved. If this is successful, a dropdown will appear with the list of ESXi hosts.", "label.prev": "Prev", "label.previous": "Previous", "label.primera.username.tooltip": "The username with edit privileges", @@ -1749,13 +1880,13 @@ "label.flashArray.username.tooltip": "The username with edit privileges", "label.flashArray.url.tooltip": "URL designating the Flash Array endpoint, formatted as: http[s]://HOSTNAME:PORT?pod=NAME&hostgroup=NAME[&skipTlsValidation=true][&postCopyWaitMs=#][&keyttl=#][&connectTimeoutMs=#][&apiLoginVersion=#][&apiVersion=#] where values in [] are optional.", "label.primary": "Primary", -"label.primary.storage": "Primary storage", -"label.primary.storage.allocated": "Primary storage allocated", -"label.primary.storage.used": "Primary storage used", -"label.primarystoragelimit": "Primary storage limits (GiB)", -"label.primarystoragetotal": "Primary storage", +"label.primary.storage": "Primary Storage", +"label.primary.storage.allocated": "Primary Storage allocated", +"label.primary.storage.used": "Primary Storage used", +"label.primarystoragelimit": "Primary Storage limits (GiB)", +"label.primarystoragetotal": "Primary Storage", "label.privatemtu": "Private Interface MTU", -"label.private.gateway": "Private gateway", +"label.private.gateway": "Private Gateway", "label.private.interface": "Private interface", "label.private.registry": "Private registry", "label.privateinterface": "Private interface", @@ -1765,7 +1896,7 @@ "label.privateport": "Private port", "label.profilename": "Profile", "label.project": "Project", -"label.project.invitation": "Project invitations", +"label.project.invitation": "Project Invitations", "label.project.name": "Project name", "label.project.owner": "Project owner(s)", "label.project.role": "Project role", @@ -1789,13 +1920,15 @@ "label.provisioningtype.fat": "Fat provisioning", "label.provisioningtype.sparse": "Sparse provisioning", "label.provisioningtype.thin": "Thin provisioning", +"label.public": "Public", "label.publicmtu": "Public Interface MTU", "label.public.interface": "Public interface", "label.public.ip": "Public IP address", -"label.public.ip.addresses": "Public IP addresses", +"label.public.ip.addresses": "Public IP Addresses", "label.public.ips": "Public IP addresses", "label.public.lb": "Public LB", "label.public.traffic": "Public traffic", +"label.public.traffic.netris": "Netris Public IP Pool", "label.public.traffic.nsx": "NSX Public traffic", "label.publicinterface": "Public interface", "label.publicip": "IP address", @@ -1859,6 +1992,7 @@ "label.read.io": "Read (IO)", "label.readonly": "Read-Only", "label.reason": "Reason", +"label.rebalance": "Rebalance", "label.reboot": "Reboot", "label.recent.deliveries": "Recent deliveries", "label.receivedbytes": "Bytes received", @@ -1873,9 +2007,12 @@ "label.redundantvpcrouter": "Redundant VPC", "label.refresh": "Refresh", "label.region": "Region", +"label.register.extension": "Register Extension", "label.register.oauth": "Register OAuth", "label.register.template": "Register Template", -"label.register.user.data": "Register a userdata", +"label.register.user.data": "Register User Data", +"label.register.cni.config": "Register CNI Configuration", +"label.register.user.data.details": "Enter the User Data in plain text or in Base64 encoding. Up to 32KB of Base64 encoded User Data can be sent by default. The setting vm.userdata.max.length can be used to increase the limit to upto 1MB.", "label.reinstall.vm": "Reinstall Instance", "label.reject": "Reject", "label.related": "Related", @@ -1883,16 +2020,17 @@ "label.release": "Release", "label.release.account": "Release from Account", "label.release.dedicated.bgp.peer": "Release dedicated BGP peer", -"label.release.dedicated.cluster": "Release dedicated cluster", +"label.release.dedicated.cluster": "Release dedicated Cluster", "label.release.dedicated.host": "Release dedicated host", "label.release.dedicated.ipv4.subnet": "Release dedicated IPv4 subnet", -"label.release.dedicated.pod": "Release dedicated pod", -"label.release.dedicated.zone": "Release dedicated zone", +"label.release.dedicated.pod": "Release dedicated Pod", +"label.release.dedicated.zone": "Release dedicated Zone", "label.releasing.ip": "Releasing IP", "label.remote.instances": "Remote Instances", "label.remove": "Remove", "label.remove.annotation": "Remove comment", "label.remove.bgp.peer": "Remove BGP peer", +"label.remove.cni.configuration": "Remove CNI configuration", "label.remove.egress.rule": "Remove egress rule", "label.remove.interface.route.table": "Remove Tungsten interface route table", "label.remove.ip.range": "Remove IP range", @@ -1900,8 +2038,9 @@ "label.remove.ldap": "Remove LDAP", "label.remove.logical.network": "Remove Network from logical router", "label.remove.logical.router": "Remove logical router", -"label.remove.network.offering": "Remove Network offering", +"label.remove.network.offering": "Remove Network Offering", "label.remove.network.route.table": "Remove Tungsten Fabric Network routing table", +"label.remove.nodes": "Remove nodes from Kubernetes cluster", "label.remove.pf": "Remove port forwarding rule", "label.remove.policy": "Remove policy", "label.remove.project.account": "Remove Account from project", @@ -1911,15 +2050,15 @@ "label.remove.rule": "Remove rule", "label.remove.ssh.key.pair": "Remove SSH Key pair", "label.remove.tungsten.tag": "Remove Tag", -"label.remove.user.data": "Remove Userdata", +"label.remove.user.data": "Remove User Data", "label.remove.vm.from.lb": "Remove Instance from load balancer rule", "label.remove.vmware.datacenter": "Remove VMware Datacenter", "label.remove.vpc": "Remove VPC", -"label.remove.vpc.offering": "Remove VPC offering", +"label.remove.vpc.offering": "Remove VPC Offering", "label.removed": "Removed", "label.removing": "Removing", +"label.replace": "Replace", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL list", "label.report.bug": "Ask a question or Report an issue", "label.request": "Request", "label.required": "Required", @@ -1938,9 +2077,10 @@ "label.reset.config.value": "Reset to default value", "label.reset.ssh.key.pair": "Reset SSH key pair", "label.reset.to.default": "Reset to default", -"label.reset.userdata.on.autoscale.vm.group": "Reset Userdata on AutoScale VM Group", -"label.reset.userdata.on.vm": "Reset Userdata on Instance", +"label.reset.user.data.on.autoscale.vm.group": "Reset User Data on AutoScale VM Group", +"label.reset.user.data.on.vm": "Reset User Data on Instance", "label.reset.vpn.connection": "Reset VPN connection", +"label.resolution": "Resolution", "label.resource": "Resource", "label.resource.limit.exceeded": "Resource limit exceeded", "label.resource.name": "Resource name", @@ -1955,12 +2095,15 @@ "label.restartrequired": "Restart required", "label.restore": "Restore", "label.restore.volume.attach": "Restore volume and attach", +"label.use.backup.ip.address": "Use IP Addresses from Backup", +"label.use.backup.ip.address.tooltip": "Use the same IP/MAC addresses as stored in the backup metadata. The command will error out if the IP/MAC addresses are not available", "label.review": "Review", "label.role": "Role", "label.roleid": "Role", "label.rolename": "Role", "label.roles": "Roles", "label.roletype": "Role Type", +"label.rolepermissiontab.searchbar": "Search Rule", "label.root.certificate": "Root certificate", "label.root.disk.size": "Root disk size (GB)", "label.rootdisk": "ROOT disk", @@ -1990,6 +2133,7 @@ "label.rules.file.import.description": "Click or drag rule definitions CSV file to import.", "label.rules.file.to.import": "Rule definitions CSV file to import", "label.run.proxy.locally": "Run proxy locally", +"label.run.custom.action": "Run Action", "label.running": "Running", "label.running.vms": "Running Instances", "label.s2scustomergatewayid": "Site to site customer gateway ID", @@ -2020,6 +2164,7 @@ "label.scaleup.policy": "ScaleUp policy", "label.scaling": "Scaling", "label.schedule": "Schedule", +"label.scheduled": "Scheduled", "label.schedule.add": "Add schedule", "label.scheduled.backups": "Scheduled backups", "label.schedules": "Schedules", @@ -2028,17 +2173,17 @@ "label.search": "Search", "label.secondary.isolated.vlan.type.isolated": "Isolated", "label.secondary.isolated.vlan.type.promiscuous": "Promiscuous", -"label.secondary.storage": "Secondary storage", -"label.secondary.storage.vm": "Secondary storage VM", +"label.secondary.storage": "Secondary Storage", +"label.secondary.storage.vm": "Secondary Storage VM", "label.secondaryips": "Secondary IPs", -"label.secondarystoragelimit": "Secondary storage limits (GiB)", +"label.secondarystoragelimit": "Secondary Storage limits (GiB)", "label.secretkey": "Secret key", "label.secured": "Secured", -"label.security.groups": "Security groups", -"label.securitygroup": "Security group", -"label.securitygroupenabled": "Security groups enabled", +"label.security.groups": "Security Groups", +"label.securitygroup": "Security Group", +"label.securitygroupenabled": "Security Groups enabled", "label.securitygroups": "Security groups", -"label.securitygroupsenabled": "Security groups enabled", +"label.securitygroupsenabled": "Security Groups enabled", "label.select": "Select", "label.see.more.info.cpu.usage": "See more info about CPU usage", "label.see.more.info.memory.usage": "See more info about memory usage", @@ -2046,18 +2191,21 @@ "label.see.more.info.disk.usage": "See more info about disk usage", "label.see.more.info.shown.charts": "See more info about the shown charts", "label.select-view": "Select view", -"label.select.a.zone": "Select a zone", +"label.select.all": "Select all", +"label.select.columns": "Select columns", +"label.select.a.zone": "Select a Zone", "label.select.deployment.infrastructure": "Select deployment infrastructure", "label.select.guest.os.type": "Please select the guest OS type", "label.select.network": "Select Network", "label.select.period": "Select period", -"label.select.project": "Select project", -"label.select.projects": "Select projects", -"label.select.ps": "Select primary storage", +"label.select.project": "Select Project", +"label.select.projects": "Select Projects", +"label.select.ps": "Select Primary Storage", "label.select.root.disk": "Select the ROOT disk", "label.select.source.vcenter.datacenter": "Select the source VMware vCenter Datacenter", "label.select.tier": "Select Network Tier", "label.select.zones": "Select zones", +"label.select.storagepools": "Select storage pools", "label.select.2fa.provider": "Select the provider", "label.selected.storage": "Selected storage", "label.self": "Mine", @@ -2068,23 +2216,26 @@ "label.sequence": "Sequence", "label.server": "Server", "label.server.certificate": "Server certificate", -"label.serviceip": "Service IP", +"label.serviceip": "Management Service IP", "label.service.connectivity.distributedroutercapabilitycheckbox": "Distributed router", "label.service.connectivity.regionlevelvpccapabilitycheckbox": "Region level VPC", -"label.service.group": "Service group", -"label.serviceip": "Management IP", +"label.service.group": "Service Group", +"label.management.ip": "Management IP", "label.service.lb.elasticlbcheckbox": "Elastic LB", "label.service.lb.inlinemodedropdown": "Mode", "label.service.lb.lbisolationdropdown": "LB isolation", "label.service.lb.netscaler.servicepackages": "Netscaler service packages", "label.service.lb.netscaler.servicepackages.description": "Service package description", -"label.service.offering": "Service offering", +"label.service.offering.controlnodes": "Compute offering for Control Nodes", +"label.service.offering.etcdnodes": "Compute offering for etcd Nodes", +"label.service.offering.workernodes": "Compute offering for Worker Nodes", +"label.service.offering": "Service Offering", "label.service.staticnat.associatepublicip": "Associate public IP", "label.service.staticnat.elasticipcheckbox": "Elastic IP", "label.servicegroupuuid": "Service Group", "label.servicelist": "Services", -"label.serviceofferingid": "Compute offering", -"label.serviceofferingname": "Compute offering", +"label.serviceofferingid": "Compute Offering", +"label.serviceofferingname": "Compute Offering", "label.sessions": "Active client sessions", "label.set.default.nic": "Set default NIC", "label.set.reservation": "Set reservation", @@ -2101,13 +2252,16 @@ "label.sharedrouteripv6": "IPv6 address for the VR in this shared Network.", "label.sharewith": "Share with", "label.showing": "Showing", +"label.show.only.gpu.enabled.offerings": "Show only GPU enabled offerings", "label.show.usage.records": "Show usage records", +"label.showing.results.for": "Showing results for \"%x\"", "label.shrinkok": "Shrink OK", "label.shutdown": "Shutdown", "label.shutdown.provider": "Shutdown provider", "label.simplified.chinese.keyboard": "Simplified Chinese keyboard", +"label.site": "Netris Site", "label.site.to.site.vpn": "Site-to-site VPN", -"label.site.to.site.vpn.connections": "Site-to-site VPN Connections", +"label.site.to.site.vpn.connections": "VPN Connections", "label.size": "Size", "label.sizegb": "Size", "label.smb.domain": "SMB domain", @@ -2152,28 +2306,41 @@ "label.srctaguuid": "Source Tag", "label.srx": "SRX", "label.srx.firewall": "Juniper SRX firewall", -"label.ssh.key.pairs": "SSH key pairs", +"label.storageaccessgroups": "Storage Access Groups", +"label.storageallocated": "Allocated size", +"label.storagetotal": "Total size", +"label.storageused": "Used size", +"label.clusterstorageaccessgroups": "Cluster Storage Access Groups", +"label.podstorageaccessgroups": "Pod Storage Access Groups", +"label.zonestorageaccessgroups": "Zone Storage Access Groups", +"label.ssh.key.pairs": "SSH Key Pairs", "label.uefi.supported": "UEFI supported", +"label.unregister.extension": "Unregister Extension", "label.usediops": "IOPS used", -"label.userdataid": "Userdata ID", -"label.userdataname": "Userdata name", -"label.userdatadetails": "Userdata details", -"label.userdataparams": "Userdata parameters", -"label.userdatapolicy": "Userdata link policy", -"label.userdata.text": "Manual Userdata entry", -"label.userdata.registered": "Stored Userdata", -"label.userdata.do.override": "Userdata override", -"label.userdata.do.append": "Userdata append", -"label.userdatapolicy.tooltip": "Userdata linked to the Template can be overridden by Userdata provided during Instance deploy. Select the override policy as required.", +"label.userdata": "User Data", +"label.user.data.id": "User Data ID", +"label.user.data.name": "User Data name", +"label.user.data.details": "User Data details", +"label.user.data.params": "User Data parameters", +"label.user.data.policy": "User Data link policy", +"label.user.data.text": "Manual User Data entry", +"label.user.data.registered": "Stored User Data", +"label.user.data.do.override": "User Data override", +"label.user.data.do.append": "User Data append", +"label.user.data.policy.tooltip": "User Data linked to the Template can be overridden by User Data provided during Instance deploy. Select the override policy as required.", "label.user.data": "User Data", +"label.user.data.library": "User Data Library", "label.ssh.port": "SSH port", "label.sshkeypair": "New SSH key pair", "label.sshkeypairs": "SSH key pairs", +"label.ssl": "SSL", +"label.sslcertificate": "SSL certificate", "label.sslcertificates": "SSL certificates", "label.sslverification": "SSL verification", "label.standard.us.keyboard": "Standard (US) keyboard", "label.start": "Start", "label.startasn": "Start AS Number", +"label.started": "Started", "label.start.date": "Start date", "label.start.date.and.time": "Start date and time", "label.start.ip": "Start IP", @@ -2192,7 +2359,7 @@ "label.state.reported": "Reported State", "label.staticnat": "Static NAT", "label.static": "Static", -"label.static.routes": "Static routes", +"label.static.routes": "Static Routes", "label.status": "Status", "label.step.1": "Step 1", "label.step.2": "Step 2", @@ -2218,12 +2385,13 @@ "label.stopping": "Stopping", "label.storage": "Storage", "label.storage.migration.required": "Storage migration required", -"label.storage.tags": "Storage tags", +"label.storage.tags": "Storage Tags", "label.storage.traffic": "Storage traffic", -"label.storageid": "Primary storage", +"label.storageid": "Primary Storage", "label.storagemotionenabled": "Storage motion enabled", "label.storagepolicy": "Storage policy", "label.storagepool": "Storage pool", +"label.storagepools": "Storage pools", "label.storagepool.tooltip": "Destination Storage Pool. Volume should be located in this Storage Pool", "label.storagetags": "Storage tags", "label.storagetype": "Storage type", @@ -2233,6 +2401,7 @@ "label.subnet": "Subnet", "label.succeeded": "Succeeded", "label.success": "Success", +"label.successmessage": "Success message", "label.success.migrations": "Successful migrations", "label.success.set": "Successfully set", "label.success.updated": "Successfully updated", @@ -2247,12 +2416,12 @@ "label.supportspublicaccess": "Supports public access", "label.supportsstrechedl2subnet": "Supports stretched L2 subnet", "label.supportsvmautoscaling": "Supports auto scaling", -"label.suspend.project": "Suspend project", +"label.suspend.project": "Suspend Project", "label.switch.type": "Switch type", -"label.sync.storage": "Sync storage pool", +"label.sync.storage": "Sync Storage Pool", "label.system.ip.pool": "System Pool", -"label.system.offering": "System offering", -"label.system.offerings": "System offerings", +"label.system.offering": "System Offering", +"label.system.offerings": "System Offerings", "label.system.service.offering": "System service offering", "label.system.vm": "System VM", "label.system.vms": "System VMs", @@ -2266,6 +2435,7 @@ "label.systemvm": "System VM", "label.systemvmtype": "System VM type", "label.tag": "Tag", +"label.tag.netris": "netris", "label.tag.nsx": "nsx", "label.tag.key": "Tag key", "label.tag.systemvm": "systemvm", @@ -2282,7 +2452,8 @@ "label.tariffvalue": "Tariff value", "label.tcp": "TCP", "label.tcp.proxy": "TCP proxy", -"label.template": "Select a template", +"label.template": "Template", +"label.template.select": "Select a template", "label.templatetag": "Tag", "label.template.select.existing": "Select an existing template", "label.template.temporary.import": "Use a temporary template for import", @@ -2296,6 +2467,7 @@ "label.templatesubject": "Subject", "label.templatetype": "Template type", "label.templateversion": "Template version", +"label.tenantname": "Netris Tenant", "label.term.type": "Term type", "label.test": "Test", "label.test.webhook.delivery": "Test Webhook Delivery", @@ -2318,6 +2490,7 @@ "label.threshold": "Threshold", "label.threshold.description": "Value for which the Counter will be evaluated with the Operator selected", "label.thursday": "Thursday", +"label.tier0gateway": "Tier-0 Gateway", "label.time": "Time", "label.timeout": "Timeout", "label.timeout.in.second ": " Timeout (seconds)", @@ -2331,11 +2504,12 @@ "label.total": "Total", "label.total.network": "Total Networks", "label.total.vms": "Total Instances", -"label.total.volume": "Total volumes", +"label.total.volume": "Total Volumes", "label.totalcpu": "Total CPU", "label.traffic.label": "Traffic label", "label.traffic.types": "Traffic types", "label.traffictype": "Traffic type", +"label.transportzone": "Transport Zone", "label.transportzoneuuid": "Transport zone UUID", "label.trigger.shutdown": "Trigger Safe Shutdown", "label.true": "True", @@ -2396,6 +2570,10 @@ "label.update.autoscale.vmgroup": "Update AutoScaling Group", "label.update.bgp.peer": "Update BGP peer", "label.update.condition": "Update condition", +"label.update.gpu.device": "Update GPU device", +"label.update.vgpu.profile": "Update vGPU profile", +"label.update.custom.action": "Update Custom Action", +"label.update.extension": "Update Extension", "label.update.sharedfs": "Update Shared FileSystem", "label.update.instance.group": "Update Instance group", "label.update.ip.range": "Update IP range", @@ -2417,10 +2595,11 @@ "label.upload.icon": "Upload icon", "label.upload.iso.from.local": "Upload ISO from local", "label.upload.resource.icon": "Upload icon", +"label.upload.ssl.certificate": "Upload SSL cerficicate", "label.upload.template.from.local": "Upload Template from local", "label.upload.volume": "Upload volume", -"label.upload.volume.from.local": "Upload volume from local", -"label.upload.volume.from.url": "Upload volume from URL", +"label.upload.volume.from.local": "Upload Volume from local", +"label.upload.volume.from.url": "Upload Volume from URL", "label.url": "URL", "label.usage.explanation": "Note: Only the usage server that owns the active usage job is shown here.", "label.usage": "Usage", @@ -2444,13 +2623,12 @@ "label.usagetypedescription": "Usage description", "label.use.kubectl.access.cluster": "kubectl and kubeconfig file to access cluster", "label.use.local.timezone": "Use local timezone", +"label.use.router.ip.resolver": "Use Virtual Router IP as resolver", "label.used": "Used", "label.usehttps": "Use HTTPS", -"label.usenewdiskoffering": "Replace disk offering?", +"label.usenewdiskoffering": "Replace Disk Offering?", "label.user": "User", "label.user.conflict": "Conflict", -"label.userdata": "Userdata", -"label.userdatal2": "User data", "label.username": "Username", "label.username.tooltip": "The Username for the Host", "label.users": "Users", @@ -2459,6 +2637,8 @@ "label.utilization": "Utilization", "label.uuid": "ID", "label.value": "Value", +"label.validationformat": "Validation Format", +"label.valueoptions": "Values Options", "label.vcenter": "VMware datacenter vCenter", "label.vcenter.datacenter": "vCenter datacenter", "label.vcenter.datastore": "vCenter datastore", @@ -2470,22 +2650,29 @@ "label.vcenterpassword": "vCenter password", "label.vcenterusername": "vCenter username", "label.vcsdeviceid": "ID", +"label.vendorid": "Vendor ID", +"label.vendorname": "Vendor Name", "label.verify": "Verify", "label.version": "Version", "label.versions": "Versions", -"label.vgpu": "VGPU", +"label.vgpu": "Profile", +"label.vgpuprofileid": "Profile", +"label.vgpuprofileids": "Profile", +"label.vgpuprofilename": "Profile", +"label.vgpu.profile": "GPU Profile", "label.vgputype": "vGPU type", +"label.videoram": "Video RAM", "label.view": "View", "label.view.all": "View all", "label.view.console": "View console", "label.viewing": "Viewing", "label.virtualmachine": "Instance", "label.virtualmachinecount": "Instances Count", -"label.virtual.machine": "Virtual machine", -"label.virtual.machines": "Virtual machines", +"label.virtual.machine": "Virtual Machine", +"label.virtual.machines": "Virtual Machines", "label.virtual.network": "Virtual Network", "label.virtual.networking": "Virtual Networking", -"label.virtual.routers": "Virtual routers", +"label.virtual.routers": "Virtual Routers", "label.virtualmachineid": "Instance ID", "label.virtualmachinename": "Instance name", "label.virtualsize": "Virtual Size", @@ -2517,21 +2704,21 @@ "label.vmwaredcvcenter": "VMware datacenter vCenter", "label.vmwarenetworklabel": "VMware traffic label", "label.vnf.appliance": "VNF Appliance", -"label.vnf.appliances": "VNF appliances", +"label.vnf.appliances": "VNF Appliances", "label.vnf.appliance.add": "Add VNF Appliance", -"label.vnf.appliance.access.methods": "Management access information for this VNF appliance", -"label.vnf.app.action.destroy": "Destroy VNF appliance", -"label.vnf.app.action.edit": "Edit VNF appliance", -"label.vnf.app.action.expunge": "Expunge VNF appliance", -"label.vnf.app.action.migrate.to.host": "Migrate VNF appliance to another host", -"label.vnf.app.action.migrate.to.ps": "Migrate VNF appliance to another primary storage", -"label.vnf.app.action.recover": "Recover VNF appliance", -"label.vnf.app.action.scale": "Scale VNF appliance", -"label.vnf.app.action.start": "Start VNF appliance", -"label.vnf.app.action.stop": "Stop VNF appliance", -"label.vnf.app.action.reboot": "Reboot VNF appliance", -"label.vnf.app.action.reinstall": "Reinstall VNF appliance", -"label.vnf.cidr.list": "CIDR from which access to the VNF appliance’s Management interface should be allowed from", +"label.vnf.appliance.access.methods": "Management access information for this VNF Appliance", +"label.vnf.app.action.destroy": "Destroy VNF Appliance", +"label.vnf.app.action.edit": "Edit VNF Appliance", +"label.vnf.app.action.expunge": "Expunge VNF Appliance", +"label.vnf.app.action.migrate.to.host": "Migrate VNF Appliance to another host", +"label.vnf.app.action.migrate.to.ps": "Migrate VNF Appliance to another primary storage", +"label.vnf.app.action.recover": "Recover VNF Appliance", +"label.vnf.app.action.scale": "Scale VNF Appliance", +"label.vnf.app.action.start": "Start VNF Appliance", +"label.vnf.app.action.stop": "Stop VNF Appliance", +"label.vnf.app.action.reboot": "Reboot VNF Appliance", +"label.vnf.app.action.reinstall": "Reinstall VNF Appliance", +"label.vnf.cidr.list": "CIDR from which access to the VNF appliance's Management interface should be allowed from", "label.vnf.cidr.list.tooltip": "the CIDR list to forward traffic from to the VNF management interface. Multiple entries must be separated by a single comma character (,). The default value is 0.0.0.0/0.", "label.vnf.configure.management": "Configure Firewall and Port Forwarding rules for VNF's management interfaces", "label.vnf.configure.management.tooltip": "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise. Learn what rules are configured at http://docs.cloudstack.apache.org/en/latest/adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances", @@ -2554,8 +2741,10 @@ "label.vnf.templates": "VNF templates", "label.vnf.template.register": "Register VNF template", "label.vnmc": "VNMC", -"label.volgroup": "Volume group", +"label.volgroup": "Volume Group", "label.volume": "Volume", +"label.vms.empty": "No VMs available to be added to the Kubernetes cluster", +"label.vms.remove.empty": "No external VMs present in the Kubernetes cluster to be removed", "label.volume.empty": "No data volumes attached to this Instance", "label.volume.encryption.support": "Volume Encryption Supported", "label.volume.metrics": "Volume Metrics", @@ -2563,7 +2752,7 @@ "label.volumechecksum": "MD5 checksum", "label.volumechecksum.description": "Use the hash that you created at the start of the volume upload procedure.", "label.volumefileupload": "Local file", -"label.volumegroup": "Volume group", +"label.volumegroup": "Volume Group", "label.volumeid": "Volume", "label.volumeids": "Volumes to be deleted", "label.volumelimit": "Volume limits", @@ -2573,21 +2762,23 @@ "label.volumetype": "Volume Type", "label.vpc": "VPC", "label.vpcs": "VPCs", +"label.vpc.gateway.ip": "VPC Gateway IP", "label.vpc.id": "VPC ID", -"label.vpc.offerings": "VPC offerings", -"label.vpc.virtual.router": "VPC virtual router", +"label.vpc.offerings": "VPC Offerings", +"label.vpc.virtual.router": "VPC Virtual Router", "label.vpc.restart.required": "VPC restart required", "label.vpcid": "VPC", "label.vpclimit": "VPC limits", "label.vpcname": "VPC", -"label.vpcoffering": "VPC offering", -"label.vpcofferingid": "VPC offering", +"label.vpcoffering": "VPC Offering", +"label.vpcofferingid": "VPC Offering", "label.vpn": "VPN", "label.vpn.connection": "VPN connection", "label.vpn.gateway": "VPN gateway", "label.vpn.users": "VPN Users", "label.vpncustomergateway": "IP address of the remote gateway", -"label.vpncustomergatewayid": "VPN customer gateway", +"label.vramsize": "VRAM Size", +"label.vpncustomergatewayid": "VPN Customer Gateway", "label.vsmipaddress": "Nexus 1000v IP address", "label.vsmpassword": "Nexus 1000v password", "label.vsmusername": "Nexus 1000v username", @@ -2622,7 +2813,7 @@ "label.xenservertoolsversion61plus": "Original XS Version is 6.1+", "label.yes": "Yes", "label.yourinstance": "Your Instance", -"label.your.autoscale.vmgroup": "Your autoscaling group", +"label.your.autoscale.vmgroup": "Your Autoscaling Group", "label.zone": "Zone", "label.zone.dedicated": "Zone dedicated", "label.zone.details": "Zone details", @@ -2635,32 +2826,60 @@ "label.zones": "Zones", "label.zonewizard.traffictype.storage": "Storage: Traffic between primary and secondary storage servers, such as Instance Templates and Snapshots.", "label.buckets": "Buckets", +"label.busaddress": "Address", "label.objectstorageid": "Object Storage Pool", +"label.oobm.address": "Out-of-band management address", +"label.oobm.driver": "Out-of-band management driver", +"label.oobm.port": "Out-of-band management port", +"label.oobm.powerstate": "Out-of-band management power state", +"label.oobm.username": "Out-of-band management username", "label.bucket.update": "Update Bucket", "label.bucket.delete": "Delete Bucket", "label.quotagib": "Quota in GiB", +"label.quotagb": "Quota in GB", +"label.edgecluster": "Edge Cluster", "label.encryption": "Encryption", +"label.etcdnodes": "Number of etcd nodes", "label.versioning": "Versioning", "label.objectlocking": "Object Lock", "label.bucket.policy": "Bucket Policy", "label.usersecretkey": "Secret Key", "label.create.bucket": "Create Bucket", +"label.cniconfiguration": "CNI Configuration", +"label.cniconfigname": "Associated CNI Configuration", +"label.cniconfigparams": "CNI Configuration parameters", +"label.create.instance.from.backup": "Create new instance from backup", +"label.lease.enable": "Enable Lease", +"label.lease.enable.tooltip": "The Instance Lease feature allows to set a lease duration (in days) for instances, after which they automatically expire. Upon expiry, the instance can either be stopped (powered off) or destroyed, based on the configured policy", +"label.instance.lease": "Instance lease", +"label.instance.lease.placeholder": "Lease duration in days ( > 0)", +"label.leaseduration": "Lease duration (in days)", +"label.leaseexpiry.date.and.time": "Lease expiry date", +"label.leaseexpiryaction": "Lease expiry action", +"label.remainingdays": "Lease", +"label.leased": "Leased", +"label.usestoragereplication": "Use primary storage replication", "message.acquire.ip.failed": "Failed to acquire IP.", "message.action.acquire.ip": "Please confirm that you want to acquire new IP.", "message.action.cancel.maintenance": "Your host has been successfully canceled for maintenance. This process can take up to several minutes.", "message.action.cancel.maintenance.mode": "Please confirm that you want to cancel this maintenance.", "message.action.create.snapshot.from.vmsnapshot": "Please confirm that you want to create Snapshot from Instance Snapshot", +"message.action.create.instance.from.backup": "Please confirm that you want to create a new Instance from the given Backup.
Click on configure to edit the parameters for the new Instance before creation.", "message.action.delete.asnrange": "Please confirm the AS range that you want to delete", "message.action.delete.autoscale.vmgroup": "Please confirm that you want to delete this autoscaling group.", "message.action.delete.backup.offering": "Please confirm that you want to delete this backup offering?", "message.action.delete.backup.repository": "Please confirm that you want to delete this backup repository?", -"message.action.delete.cluster": "Please confirm that you want to delete this cluster.", +"message.action.delete.cluster": "Please confirm that you want to delete this Cluster.", +"message.action.delete.custom.action": "Please confirm that you want to delete this custom action.", "message.action.delete.domain": "Please confirm that you want to delete this domain.", +"message.action.delete.extension": "Please confirm that you want to delete the extension", "message.action.delete.external.firewall": "Please confirm that you would like to remove this external firewall. Warning: If you are planning to add back the same external firewall, you must reset usage data on the device.", "message.action.delete.external.load.balancer": "Please confirm that you would like to remove this external load balancer. Warning: If you are planning to add back the same external load balancer, you must reset usage data on the device.", "message.action.delete.ingress.rule": "Please confirm that you want to delete this ingress rule.", "message.action.delete.ipv4.subnet": "Please confirm that you want to delete this IPv4 subnet.", +"message.action.delete.gpu.card": "Please confirm that you want to delete this GPU card.", "message.action.delete.guest.os": "Please confirm that you want to delete this guest os. System defined entry cannot be deleted.", +"message.action.delete.guest.os.category": "Please confirm that you want to delete this guest os category.", "message.action.delete.guest.os.hypervisor.mapping": "Please confirm that you want to delete this guest os hypervisor mapping. System defined entry cannot be deleted.", "message.action.delete.instance.group": "Please confirm that you want to delete the Instance group.", "message.action.delete.interface.static.route": "Please confirm that you want to remove this interface Static Route?", @@ -2671,15 +2890,16 @@ "message.action.delete.node": "Please confirm that you want to delete this node.", "message.action.delete.oauth.provider": "Please confirm that you want to delete the OAuth provider.", "message.action.delete.physical.network": "Please confirm that you want to delete this physical Network.", -"message.action.delete.pod": "Please confirm that you want to delete this pod.", +"message.action.delete.pod": "Please confirm that you want to delete this Pod.", "message.action.delete.secondary.storage": "Please confirm that you want to delete this secondary storage.", "message.action.delete.security.group": "Please confirm that you want to delete this security group.", "message.action.delete.snapshot": "Please confirm that you want to delete this Snapshot.", "message.action.delete.template": "Please confirm that you want to delete this Template.", "message.action.delete.tungsten.router.table": "Please confirm that you want to remove Route Table from this Network?", +"message.action.delete.vgpu.profile": "Please confirm that you want to delete this vGPU profile.", "message.action.delete.volume": "Please confirm that you want to delete this volume. Note: this will not delete any Snapshots of this volume.", "message.action.delete.vpn.user": "Please confirm that you want to delete the VPN user.", -"message.action.delete.zone": "Please confirm that you want to delete this zone.", +"message.action.delete.zone": "Please confirm that you want to delete this Zone.", "message.action.destroy.sharedfs": "Please confirm that you want to destroy this Shared FileSystem.
Caution: This will delete all the data of the Shared FileSystem as well.", "message.action.destroy.instance": "Please confirm that you want to destroy the Instance.", "message.action.destroy.instance.with.backups": "Please confirm that you want to destroy the Instance. There may be backups associated with the Instance which will not be deleted.", @@ -2687,35 +2907,36 @@ "message.action.destroy.volume": "Please confirm that you want to destroy the volume.", "message.action.disable.2FA.user.auth": "Please confirm that you want to disable User two factor authentication.", "message.action.about.mandate.and.disable.2FA.user.auth": "Two factor authentication is mandated for the User, if this is disabled now User will need to setup two factor authentication again during next login.

Please confirm that you want to disable.", -"message.action.disable.cluster": "Please confirm that you want to disable this cluster.", +"message.action.disable.cluster": "Please confirm that you want to disable this Cluster.", "message.action.disable.disk.offering": "Please confirm that you want to disable this disk offering.", "message.action.disable.service.offering": "Please confirm that you want to disable this service offering.", "message.action.disable.system.service.offering": "Please confirm that you want to disable this system service offering.", "message.action.disable.physical.network": "Please confirm that you want to disable this physical Network.", -"message.action.disable.pod": "Please confirm that you want to disable this pod.", +"message.action.disable.pod": "Please confirm that you want to disable this Pod.", "message.action.disable.static.nat": "Please confirm that you want to disable static NAT.", -"message.action.disable.zone": "Please confirm that you want to disable this zone.", +"message.action.disable.zone": "Please confirm that you want to disable this Zone.", +"message.action.discover.gpu.devices": "Please confirm that you want to discover GPU devices.", "message.action.download.iso": "Please confirm that you want to download this ISO.", "message.action.download.snapshot": "Please confirm that you want to download this Snapshot.", "message.action.download.template": "Please confirm that you want to download this Template.", "message.action.edit.nfs.mount.options": "Changes to NFS mount options will only take affect on cancelling maintenance mode which will cause the storage pool to be remounted on all KVM hosts with the new mount options.", -"message.action.enable.cluster": "Please confirm that you want to enable this cluster.", +"message.action.enable.cluster": "Please confirm that you want to enable this Cluster.", "message.action.enable.disk.offering": "Please confirm that you want to enable this disk offering.", "message.action.enable.service.offering": "Please confirm that you want to enable this service offering.", "message.action.enable.system.service.offering": "Please confirm that you want to enable this system service offering.", "message.action.enable.physical.network": "Please confirm that you want to enable this physical Network.", -"message.action.enable.pod": "Please confirm that you want to enable this pod.", -"message.action.enable.zone": "Please confirm that you want to enable this zone.", +"message.action.enable.pod": "Please confirm that you want to enable this Pod.", +"message.action.enable.zone": "Please confirm that you want to enable this Zone.", "message.action.expunge.sharedfs": "Please confirm that you want to expunge this Shared FileSystem.", "message.action.expunge.instance": "Please confirm that you want to expunge this Instance.", "message.action.expunge.instance.with.backups": "Please confirm that you want to expunge this Instance. There may be backups associated with the Instance which will not be deleted.", "message.action.host.enable.maintenance.mode": "Enabling maintenance mode will cause a live migration of all running Instances on this host to any available host.", "message.action.instance.reset.password": "Please confirm that you want to change the ROOT password for this Instance.", -"message.action.manage.cluster": "Please confirm that you want to manage the cluster.", +"message.action.manage.cluster": "Please confirm that you want to manage the Cluster.", "message.action.patch.router": "Please confirm that you want to live patch the router.
This operation is equivalent updating the router packages and restarting the Network without cleanup.", "message.action.patch.systemvm": "Please confirm that you want to patch the System VM.", -"message.action.primary.storage.scope.cluster": "Please confirm that you want to change the scope from zone to the specified cluster.
This operation will update the database and disconnect the storage pool from all hosts that were previously connected to the primary storage and are not part of the specified cluster.", -"message.action.primary.storage.scope.zone": "Please confirm that you want to change the scope from cluster to zone.
This operation will update the database and connect the storage pool to all hosts of the zone running the same hypervisor as set on the storage pool.", +"message.action.primary.storage.scope.cluster": "Please confirm that you want to change the scope from Zone to the specified Cluster.
This operation will update the database and disconnect the storage pool from all hosts that were previously connected to the primary storage and are not part of the specified cluster.", +"message.action.primary.storage.scope.zone": "Please confirm that you want to change the scope from Cluster to Zone.
This operation will update the database and connect the storage pool to all hosts of the zone running the same hypervisor as set on the storage pool.", "message.action.primarystorage.enable.maintenance.mode": "Warning: placing the primary storage into maintenance mode will cause all Instances using volumes from it to be stopped. Do you want to continue?", "message.action.quota.tariff.create.error.namerequired": "Please, inform a name for the quota tariff.", "message.action.quota.tariff.create.error.usagetyperequired": "Please, select the usage type of the quota tariff.", @@ -2726,6 +2947,7 @@ "message.action.reboot.systemvm": "Please confirm that you want to reboot this system VM.", "message.action.recover.sharedfs": "Please confirm that you would like to recover this Shared FileSystem.", "message.action.recover.volume": "Please confirm that you would like to recover this volume.", +"message.action.register.extension": "Register the extension to a resource", "message.action.release.asnumber": "Please confirm that you want to release this AS Number.", "message.action.release.ip": "Please confirm that you want to release this IP.", "message.action.remove.host": "Please confirm that you want to remove this host.", @@ -2737,7 +2959,7 @@ "message.action.revert.snapshot": "Please confirm that you want to revert the owning volume to this Snapshot.", "message.action.router.health.checks": "Health checks result will be fetched from router.", "message.action.router.health.checks.disabled.warning": "Please enable router health checks.", -"message.action.scale.kubernetes.cluster.warning": "Please do not manually scale the cluster if cluster auto scaling is enabled.", +"message.action.scale.kubernetes.cluster.warning": "Please do not manually scale the Cluster if cluster auto scaling is enabled.", "message.action.secondary.storage.read.only": "Please confirm that you want to make this secondary storage read only.", "message.action.secondary.storage.read.write": "Please confirm that you want to make this secondary storage read write.", "message.action.secure.host": "This will restart the host agent and libvirtd process after applying new X509 certificates, please confirm?", @@ -2750,7 +2972,7 @@ "message.action.stop.instance": "Please confirm that you want to stop this Instance.", "message.action.stop.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router.", "message.action.stop.systemvm": "Please confirm that you want to stop this system VM.", -"message.action.unmanage.cluster": "Please confirm that you want to unmanage the cluster.", +"message.action.unmanage.cluster": "Please confirm that you want to unmanage the Cluster.", "message.action.unmanage.instance": "Please confirm that you want to unmanage the Instance.", "message.action.unmanage.instances": "Please confirm that you want to unmanage the Instances.", "message.action.unmanage.virtualmachine": "Please confirm that you want to unmanage the Instance.", @@ -2758,14 +2980,19 @@ "message.action.unmanage.volumes": "Please confirm that you want to unmanage the Volumes.", "message.action.vmsnapshot.delete": "Please confirm that you want to delete this Instance Snapshot.
Please notice that the Instance will be paused before the Snapshot deletion, and resumed after deletion, if it runs on KVM.", "message.activate.project": "Are you sure you want to activate this project?", +"message.add.custom.action.parameters": "Parameters to be made available while running the custom action.", "message.add.egress.rule.failed": "Adding new egress rule failed.", "message.add.egress.rule.processing": "Adding new egress rule...", "message.add.failed": "Adding failed.", -"message.add.firewall": "Add a firewall to zone", +"message.add.firewall": "Add a firewall to Zone", "message.add.firewall.rule.failed": "Adding new Firewall rule failed", "message.add.firewall.rule.processing": "Adding new Firewall rule...", "message.add.firewallrule.failed": "Adding Firewall Rule failed", "message.add.host": "Please specify the following parameters to add a new host.", +"message.add.extension.custom.action.details": "Details to be sent to the extension during execution of this action.", +"message.add.extension.details": "Details to be sent to the extension on any operation.", +"message.add.extension.resource.details": "Details to be sent to the extension during any operation for this resource.", +"message.add.orchestrator.resource.details": "Details to be sent to the hypervisor.", "message.add.host.sshkey": "WARNING: In order to add a host with SSH key, you must ensure your hypervisor host has been configured correctly.", "message.add.iprange.processing": "Adding IP Range...", "message.add.ipv4.subnet.for.guest.network.failed": "Failed to add IPv4 subnet for guest network", @@ -2778,23 +3005,26 @@ "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule", "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...", "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule", +"message.remove.sslcert.failed": "Failed to remove SSL certificate from load balancer", +"message.remove.sslcert.processing": "Removing SSL certificate from load balancer...", +"message.add.netris.controller": "Add Netris Provider", "message.add.nsx.controller": "Add NSX Provider", -"message.add.network": "Add a new network for zone: ", -"message.add.network.acl.failed": "Adding network ACL list failed.", -"message.add.network.acl.processing": "Adding network ACL list...", +"message.add.network": "Add a new network for Zone: ", +"message.add.network.acl.failed": "Adding network ACL failed.", +"message.add.network.acl.processing": "Adding network ACL...", "message.add.network.failed": "Adding network failed.", "message.add.network.processing": "Adding network...", "message.add.new.gateway.to.vpc": "Please specify the information to add a new gateway to this VPC.", "message.add.physical.network.failed": "Adding physical network failed", "message.add.physical.network.processing": "Adding a new physical network...", -"message.add.pod": "Add a new pod for zone ", -"message.add.pod.during.zone.creation": "Each zone must contain one or more pods. We will add the first pod now. A pod contains hosts and primary storage servers, which you will add in a later step. First, configure a range of reserved IP addresses for CloudStack's internal management traffic. The reserved IP range must be unique for each zone in the cloud.", +"message.add.pod": "Add a new Pod for Zone ", +"message.add.pod.during.zone.creation": "Each Zone must contain one or more Pods. We will add the first pod now. A pod contains hosts and primary storage servers, which you will add in a later step. First, configure a range of reserved IP addresses for CloudStack's internal management traffic. The reserved IP range must be unique for each zone in the cloud.", "message.add.port.forward.failed": "Adding new port forwarding rule failed.", "message.add.port.forward.processing": "Adding new port forwarding rule...", "message.add.private.gateway.failed": "Adding private gateway failed.", "message.add.private.gateway.processing": "Adding private gateway...", "message.add.resource.description": "Add infrastructure resources", -"message.add.resource.hint": "Add infrastructure resources - pods, clusters, primary/secondary storages.", +"message.add.resource.hint": "Add infrastructure resources - Pods, Clusters, primary/secondary storages.", "message.add.routing.firewall.rule.failed": "Failed to add IPv4 Routing firewall rule", "message.add.routing.firewall.rule.processing": "Adding IPv4 Routing firewall rule...", "message.add.routing.firewall.rule.success": "Added IPv4 Routing firewall rule", @@ -2822,10 +3052,14 @@ "message.adding.host": "Adding host", "message.adding.netscaler.device": "Adding Netscaler device", "message.adding.netscaler.provider": "Adding Netscaler provider", +"message.adding.nodes.to.cluster": "Adding nodes to Kubernetes cluster", +"message.removing.nodes.from.cluster": "Removing nodes from Kubernetes cluster", "message.advanced.security.group": "Choose this if you wish to use security groups to provide guest Instance isolation.", "message.allowed": "Allowed", "message.alert.show.all.stats.data": "This may return a lot of data depending on VM statistics and retention settings", "message.apply.success": "Apply Successfully", +"message.assign.sslcert.failed": "Failed to assign SSL certificate", +"message.assign.sslcert.processing": "Assigning SSL certificate...", "message.assign.instance.another": "Please specify the Account type, domain, Account name and Network (optional) of the new Account.
If the default NIC of the Instance is on a shared Network, CloudStack will check if the Network can be used by the new Account if you do not specify one Network.
If the default NIC of the Instance is on a isolated Network, and the new Account has more one isolated Networks, you should specify one.", "message.assign.vm.failed": "Failed to assign Instance", "message.assign.vm.processing": "Assigning Instance...", @@ -2834,12 +3068,12 @@ "message.attach.volume.progress": "Attaching volume", "message.attach.volume.success": "Successfully attached the volume to the instance", "message.authorization.failed": "Session expired, authorization verification failed.", -"message.autoscale.loadbalancer.update": "The load balancer rule can be updated only when autoscaling group is DISABLED.", +"message.autoscale.loadbalancer.update": "The load balancer rule can be updated. However, instance can be removed only when autoscaling group is DISABLED.", "message.autoscale.policies.update": "The scale up/down policies can be updated only when autoscaling group is DISABLED.", "message.autoscale.vm.networks": "Please choose at least one Network for Instances in the autoscaling group. The default Network must be an Isolated Network or VPC Network Tier which supports Instance AutoScaling and has load balancing rules.", "message.autoscale.vmprofile.update": "The autoscale Instance profile can be updated only when autoscaling group is DISABLED.", "message.backup.attach.restore": "Please confirm that you want to restore and attach the volume from the backup?", -"message.backup.create": "Are you sure you want create an Instance backup?", +"message.backup.create": "Are you sure you want to create an Instance backup?", "message.backup.offering.remove": "Are you sure you want to remove Instance from backup offering and delete the backup chain?", "message.backup.restore": "Please confirm that you want to restore the Instance backup?", "message.cancel.shutdown": "Please confirm that you would like to cancel the shutdown on this Management Server. It will resume accepting any new Async Jobs.", @@ -2866,6 +3100,8 @@ "message.configuring.guest.traffic": "Configuring guest traffic", "message.configuring.physical.networks": "Configuring physical Networks", "message.configuring.public.traffic": "Configuring public traffic", +"message.configuring.netris.public.traffic": "Configuring Netris public traffic", +"message.configuring.storage.access.failed": "Configuring storage access failed", "message.configuring.nsx.public.traffic": "Configuring NSX public traffic", "message.configuring.storage.traffic": "Configuring storage traffic", "message.confirm.action.force.reconnect": "Please confirm that you want to force reconnect this host.", @@ -2878,11 +3114,13 @@ "message.confirm.change.offering.for.volume": "Please confirm that you want to change disk offering for the volume", "message.confirm.change.service.offering.for.sharedfs": "Please confirm that you want to change the service offering for the Shared FileSystem.", "message.confirm.configure.ovs": "Are you sure you want to configure Ovs?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller.", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch.", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v.", "message.confirm.delete.ciscovnmc.resource": "Please confirm you want to delete CiscoVNMC resource.", +"message.confirm.delete.gpu.devices": "Please confirm that you would like to delete this GPU device?", +"message.confirm.delete.vgpu.profile": "Please confirm that you want to delete this vGPU profile?", "message.confirm.delete.f5": "Please confirm that you would like to delete F5.", "message.confirm.delete.internal.lb": "Please confirm you want to delete internal LB.", "message.confirm.delete.netscaler": "Please confirm that you would like to delete NetScaler.", @@ -2893,19 +3131,25 @@ "message.confirm.delete.traffic.type": "Please confirm that you would like to delete traffic type.", "message.confirm.destroy.router": "All services provided by this virtual router will be interrupted. Please confirm that you want to stop this router. Please confirm that you would like to destroy this router.", "message.confirm.disable.autoscale.vmgroup": "Please confirm that you want to disable this autoscaling group.", +"message.confirm.disable.custom.action": "Please confirm that you want to disable this custom action.", +"message.confirm.disable.extension": "Please confirm that you want to disable this extension.", "message.confirm.disable.host": "Please confirm that you want to disable the host.", "message.confirm.disable.network.offering": "Are you sure you want to disable this Network offering?", "message.confirm.disable.provider": "Please confirm that you would like to disable this provider.", "message.confirm.disable.storage": "Please confirm that you want to disable the storage pool.", "message.confirm.disable.vpc.offering": "Are you sure you want to disable this VPC offering?", "message.confirm.disable.webhook": "Please confirm that you want to disable this webhook.", +"message.confirm.discover.gpu.devices": "Please confirm that you want to discover GPU devices?", "message.confirm.enable.autoscale.vmgroup": "Please confirm that you want to enable this autoscaling group.", +"message.confirm.enable.custom.action": "Please confirm that you want to enable this custom action.", +"message.confirm.enable.extension": "Please confirm that you want to enable this extension.", "message.confirm.enable.host": "Please confirm that you want to enable the host.", "message.confirm.enable.network.offering": "Are you sure you want to enable this Network offering?", "message.confirm.enable.provider": "Please confirm that you would like to enable this provider.", "message.confirm.enable.storage": "Please confirm that you want to enable the storage pool.", "message.confirm.enable.vpc.offering": "Are you sure you want to enable this VPC offering?", "message.confirm.enable.webhook": "Please confirm that you want to enable this webhook.", +"message.confirm.manage.gpu.devices": "Please confirm that you want to manage the selected GPU devices?", "message.confirm.remove.firewall.rule": "Please confirm that you want to delete this Firewall Rule?", "message.confirm.remove.ip.range": "Please confirm that you would like to remove this IP range.", "message.confirm.remove.network.offering": "Are you sure you want to remove this Network offering?", @@ -2916,20 +3160,24 @@ "message.confirm.remove.vmware.datacenter": "Please confirm you want to remove VMware datacenter.", "message.confirm.remove.vpc.offering": "Are you sure you want to remove this VPC offering?", "message.confirm.replace.acl.new.one": "Do you want to replace the ACL with a new one?", +"message.confirm.reset.configuration.value": "Are you sure you want reset configuration - %x?", "message.confirm.reset.network.permissions": "Are you sure you want to reset this Network permissions?", "message.confirm.scale.up.router.vm": "Do you really want to scale up the router Instance?", "message.confirm.scale.up.system.vm": "Do you really want to scale up the system VM?", "message.confirm.start.lb.vm": "Please confirm you want to start LB Instance.", "message.confirm.sync.storage": "Please confirm you want to sync the storage pool", "message.confirm.type": "To confirm, please type", +"message.confirm.unmanage.gpu.devices": "Please confirm that you want to unmanage the selected GPU devices?", "message.confirm.upgrade.router.newer.template": "Please confirm that you want to upgrade router to use newer Template.", "message.cpu.usage.info": "The CPU usage percentage can exceed 100% if the Instance has more than 1 vCPU or when CPU Cap is not enabled. This behavior happens according to the hypervisor being used (e.g: in KVM), due to how they account the stats", +"message.create.backup.failed": "Failed to create backup.", "message.create.bucket.failed": "Failed to create bucket.", "message.create.bucket.processing": "Bucket creation in progress", -"message.create.compute.offering": "Compute offering created", +"message.create.compute.offering": "Compute Offering created", "message.create.sharedfs.failed": "Failed to create Shared FileSystem.", "message.create.sharedfs.processing": "Shared FileSystem creation in progress.", "message.create.tungsten.public.network": "Create Tungsten-Fabric public Network", +"message.create.instance.from.backup.prefill": "Data is prefilled using the configurations stored in the backup. Edit to change individual fields.", "message.create.internallb": "Creating internal LB", "message.create.internallb.failed": "Failed to create internal LB.", "message.create.internallb.processing": "Creation of internal LB is in progress", @@ -2938,7 +3186,7 @@ "message.create.snapshot.from.vmsnapshot.progress": "Snapshot creation in progress", "message.create.template.failed": "Failed to create template.", "message.create.template.processing": "Template creation in progress", -"message.create.volume.failed": "Failed to create volume.", +"message.create.volume.failed": "Failed to create Volume.", "message.create.volume.processing": "Volume creation in progress", "message.create.vpc.offering": "VPC offering created.", "message.create.vpn.customer.gateway.failed": "VPN customer gateway creation failed.", @@ -2948,24 +3196,28 @@ "message.creating.autoscale.scaledown.policy": "Creating ScaleDown policy", "message.creating.autoscale.scaleup.conditions": "Creating ScaleUp conditions", "message.creating.autoscale.scaleup.policy": "Creating ScaleUp policy", -"message.creating.cluster": "Creating cluster", +"message.creating.cluster": "Creating Cluster", "message.creating.guest.network": "Creating guest Network", "message.creating.physical.networks": "Creating physical Networks", -"message.creating.pod": "Creating pod", +"message.creating.pod": "Creating Pod", "message.creating.primary.storage": "Creating primary storage", "message.creating.secondary.storage": "Creating secondary storage", -"message.creating.zone": "Creating zone", +"message.creating.zone": "Creating Zone", "message.data.migration": "Data migration", "message.data.migration.progress": "Data migration between image stores", "message.datacenter.description": "Name of the datacenter on vCenter.", "message.datastore.description": "Name of the datastore on vCenter.", -"message.dedicate.zone": "Dedicating zone", +"message.dedicate.zone": "Dedicating Zone", "message.dedicated.zone.released": "Zone dedication released.", -"message.dedicating.cluster": "Dedicating cluster...", +"message.dedicating.cluster": "Dedicating Cluster...", "message.dedicating.host": "Dedicating host...", -"message.dedicating.pod": "Dedicating pod...", -"message.dedicating.zone": "Dedicating zone...", -"message.delete.account": "Please confirm that you want to delete this Account.", +"message.dedicating.pod": "Dedicating Pod...", +"message.dedicating.zone": "Dedicating Zone...", +"message.delete.account.confirm": "Please confirm that you want to delete this account by entering the name of the account below.", +"message.delete.account.failed": "Delete account failed", +"message.delete.account.processing": "Deleting account", +"message.delete.account.success": "Successfully deleted account", +"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.", "message.delete.acl.processing": "Removing ACL rule...", "message.delete.acl.rule": "Remove ACL rule", "message.delete.acl.rule.failed": "Failed to remove ACL rule.", @@ -2990,6 +3242,7 @@ "message.delete.vpn.connection": "Please confirm that you want to delete VPN connection.", "message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN customer gateway.", "message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway.", +"message.delete.vpn.gateway.failed": "Failed to delete VPN Gateway.", "message.delete.webhook": "Please confirm that you want to delete this Webhook.", "message.delete.webhook.delivery": "Please confirm that you want to delete this Webhook delivery.", "message.deleting.firewall.policy": "Deleting Firewall Policy", @@ -2999,27 +3252,31 @@ "message.deployasis": "Selected Template is Deploy As-Is i.e., the Instance is deployed by importing an OVA with vApps directly into vCenter. Root disk(s) resize is allowed only on stopped Instances for such Templates.", "message.desc.advanced.zone": "This is recommended and allows more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.", "message.desc.basic.zone": "Provide a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering).", -"message.desc.core.zone": "Core Zones are intended for Datacenter based deployments and allow the full range of Networking and other functionality in Apache CloudStack. Core zones have a number of prerequisites and rely on the presence of shared storage and helper Instances.", -"message.desc.edge.zone": "Edge Zones are lightweight zones, designed for deploying in edge computing scenarios. They are limited in functionality but have far fewer prerequisites than core zones.

Please refer to the Apache CloudStack documentation for more information on Zone Types
http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone", -"message.desc.cluster": "Each pod must contain one or more clusters. We will add the first cluster now. A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Each cluster consists of one or more hosts and one or more primary storage servers.", +"message.desc.core.zone": "Core Zones are intended for Datacenter based deployments and allow the full range of Networking and other functionality in Apache CloudStack. Core Zones have a number of prerequisites and rely on the presence of shared storage and helper Instances.", +"message.desc.edge.zone": "Edge Zones are lightweight Zones, designed for deploying in edge computing scenarios. They are limited in functionality but have far fewer prerequisites than core zones.

Please refer to the Apache CloudStack documentation for more information on Zone Types
http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone", +"message.desc.cluster": "Each Pod must contain one or more Clusters. We will add the first cluster now. A cluster provides a way to group hosts. The hosts in a cluster all have identical hardware, run the same hypervisor, are on the same subnet, and access the same shared storage. Each cluster consists of one or more hosts and one or more primary storage servers.", "message.desc.create.ssh.key.pair": "Please fill in the following data to create or register a ssh key pair.

(1) If public key is set, CloudStack will register the public key. You can use it through your private key.

(2) If public key is not set, CloudStack will create a new SSH key pair. In this case, please copy and save the private key. CloudStack will not keep it.
", "message.desc.created.ssh.key.pair": "Created a SSH key pair.", -"message.desc.host": "Each cluster must contain at least one host (computer) for guest Instances to run on. We will add the first host now. For a host to function in CloudStack, you must install hypervisor software on the host, assign an IP address to the host, and ensure the host is connected to the CloudStack management server.

Give the host's DNS or IP address, the user name (usually root) and password, and any labels you use to categorize hosts.", -"message.desc.importingestinstancewizard": "This feature only applies to libvirt based KVM instances. Only Stopped instances can be ingested", +"message.desc.host": "Each Cluster must contain at least one host (computer) for guest Instances to run on. We will add the first host now. For a host to function in CloudStack, you must install hypervisor software on the host, assign an IP address to the host, and ensure the host is connected to the CloudStack management server.

Give the host's DNS or IP address, the user name (usually root) and password, and any labels you use to categorize hosts.", "message.desc.import.ext.kvm.wizard": "Import libvirt domain from External KVM Host not managed by CloudStack", "message.desc.import.local.kvm.wizard": "Import QCOW2 image from Local Storage of selected KVM Host", "message.desc.import.shared.kvm.wizard": "Import QCOW2 image from selected Primary Storage Pool", "message.desc.import.unmanage.volume": "Please choose a storage pool that you want to import or unmanage volumes. The storage pool should be in Up status.
This feature only supports KVM.", "message.desc.importexportinstancewizard": "By choosing to manage an Instance, CloudStack takes over the orchestration of that Instance. Unmanaging an Instance removes CloudStack ability to manage it. In both cases, the Instance is left running and no changes are done to the VM on the hypervisor.

For KVM, managing a VM is an experimental feature.", -"message.desc.importmigratefromvmwarewizard": "By selecting an existing or external VMware Datacenter and an instance to import, CloudStack migrates the selected instance from VMware to KVM on a conversion host using virt-v2v and imports it into a KVM cluster", -"message.desc.primary.storage": "Each cluster must contain one or more primary storage servers. We will add the first one now. Primary storage contains the disk volumes for all the Instances running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.", -"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you would like to add to this Instance.", -"message.desc.secondary.storage": "Each zone must have at least one NFS or secondary storage server. We will add the first one now. Secondary storage stores Instance Templates, ISO images, and Instance disk volume Snapshots. This server must be available to all hosts in the zone.

Provide the IP address and exported path.", -"message.desc.register.user.data": "Please fill in the following data to register a User data.", +"message.desc.importingestinstancewizard": "This feature only applies to libvirt based KVM instances. Only Stopped instances can be ingested", +"message.desc.importmigratefromvmwarewizard": "By selecting an existing or external VMware Datacenter and an instance to import, CloudStack migrates the selected instance from VMware to KVM on a conversion host using virt-v2v and imports it into a KVM Cluster", +"message.desc.primary.storage": "Each Cluster must contain one or more primary storage servers. We will add the first one now. Primary storage contains the disk volumes for all the Instances running on hosts in the cluster. Use any standards-compliant protocol that is supported by the underlying hypervisor.", +"message.desc.register.template": "Hosted on download.cloudstack.org, these templates can be easily registered directly within CloudStack. Simply click Register Template for the templates you wish to use.", +"message.desc.register.cni.config": "Please fill in the following data to register CNI Configuration as user data.", +"message.desc.register.user.data": "Please fill in the following to register new User Data.", "message.desc.registered.user.data": "Registered a User Data.", -"message.desc.zone": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", -"message.desc.zone.edge": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. An edge zone consists of one or more hosts (each of which provides local storage as primary storage servers). Only shared and L2 Networks can be deployed in such zones and functionalities that require secondary storages are not supported.", -"message.drs.plan.description": "The maximum number of live migrations allowed for DRS. Configure DRS under the settings tab before generating a plan or to enable automatic DRS for the cluster.", +"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you would like to add to this Instance.", +"message.desc.secondary.storage": "Each Zone must have at least one NFS or secondary storage server. We will add the first one now. Secondary storage stores Instance Templates, ISO images, and Instance disk volume Snapshots. This server must be available to all hosts in the zone.

Provide the IP address and exported path.", +"message.desc.validationformat": "Specifies the format used to validate the parameter value, such as EMAIL, URL, UUID, DECIMAL, etc.", +"message.desc.valueoptions": "Provide a comma-separated list of values that will appear as selectable options for this parameter", +"message.desc.zone": "A Zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more Pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", +"message.desc.zone.edge": "A Zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. An edge zone consists of one or more hosts (each of which provides local storage as primary storage servers). Only shared and L2 Networks can be deployed in such zones and functionalities that require secondary storages are not supported.", +"message.drs.plan.description": "The maximum number of live migrations allowed for DRS. Configure DRS under the settings tab before generating a plan or to enable automatic DRS for the Cluster.", "message.drs.plan.executed": "DRS plan executed successfully.", "message.zone.edge.local.storage": "Local storage will be used by default for User Instances and virtual routers", "message.detach.disk": "Are you sure you want to detach this disk?", @@ -3053,11 +3310,12 @@ "message.enable.vpn": "Please confirm that you want remote access VPN enabled for this IP address.", "message.enable.vpn.failed": "Failed to enable VPN.", "message.enable.vpn.processing": "Enabling VPN...", -"message.enabled.vpn": "Your remote access VPN is currently enabled and can be accessed via the IP.", +"message.enabled.vpn": "Your remote access VPN is currently enabled and can be accessed via the IP", "message.enabled.vpn.ip.sec": "Your IPSec pre-shared key is", "message.enabling.security.group.provider": "Enabling security group provider", "message.enter.valid.nic.ip": "Please enter a valid IP address for NIC", "message.error.access.key": "Please enter access key.", +"message.error.account.delete.name.mismatch": "Name entered doesn't match the account name.", "message.error.add.guest.network": "Either IPv4 fields or IPv6 fields need to be filled when adding a guest Network.", "message.error.add.interface.static.route": "Adding interface Static Route failed", "message.error.add.logical.router": "Adding Logical Router failed", @@ -3076,8 +3334,8 @@ "message.error.cidr": "CIDR is required", "message.error.cidr.or.cidrsize": "CIDR or cidr size is required", "message.error.cloudian.console": "Single-Sign-On failed for Cloudian management console. Please ask your administrator to fix integration issues.", -"message.error.cluster.description": "Please enter Kubernetes cluster description.", -"message.error.cluster.name": "Please enter cluster name.", +"message.error.cluster.description": "Please enter Kubernetes Cluster description.", +"message.error.cluster.name": "Please enter Cluster name.", "message.error.confirm.password": "Please confirm new password.", "message.error.confirm.text": "Please enter the confirmation text", "message.error.current.password": "Please enter current password.", @@ -3106,6 +3364,9 @@ "message.error.host.username": "Please enter host username.", "message.error.hypervisor.type": "Please select hypervisor type.", "message.error.input.value": "Please enter value.", +"message.error.input.invalidemail": "Please enter a valid email.", +"message.error.input.invalidurl": "Please enter a valid URL.", +"message.error.input.invaliduuid": "Please enter a valid UUID.", "message.error.internal.dns1": "Please enter internal DNS 1", "message.error.internallb.instance.port": "Please specify a Instance port.", "message.error.internallb.name": "Please specify a name for the internal LB.", @@ -3118,7 +3379,7 @@ "message.error.ipv6.address": "Please enter a valid IP v6 address.", "message.error.ipv6.gateway": "Please enter IpV6 Gateway", "message.error.ipv6.gateway.format": "Please enter a valid IPv6 Gateway.", -"message.error.kubecluster.name": "Please enter Kubernetes cluster name.", +"message.error.kubecluster.name": "Please enter Kubernetes Cluster name.", "message.error.kuberversion": "Please enter Kubernetes semantic version.", "message.error.limit.value": "The value must not be less than", "message.error.loading.setting": "There was an error loading these settings.", @@ -3152,7 +3413,7 @@ "message.error.remove.vm.schedule": "Removing Instance Schedule failed", "message.error.required.input": "Please enter input", "message.error.reset.config": "Unable to reset config to default value", -"message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes cluster config", +"message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes Cluster config", "message.error.routing.policy.term": "Community need to have the following format number:number", "message.error.s3nfs.path": "Please enter S3 NFS Path", "message.error.s3nfs.server": "Please enter S3 NFS Server", @@ -3169,12 +3430,13 @@ "message.error.sbdomain.username": "Please enter SMB domain username.", "message.error.secret.key": "Please enter secret key.", "message.error.select": "Please select option.", +"message.error.select.account.to.dedicate": "Please select an account to dedicate to.", "message.error.select.domain.to.dedicate": "Please select domain to dedicate to.", -"message.error.select.zone.type": "Please select zone type below.", +"message.error.select.zone.type": "Please select Zone type below.", "message.error.server": "Please enter server.", -"message.error.serviceoffering.for.cluster": "Please select service offering for Kubernetes cluster.", +"message.error.serviceoffering.for.cluster": "Please select service offering for Kubernetes Cluster.", "message.error.size": "Please enter size in GB.", -"message.error.size.for.cluster": "Please enter size for Kubernetes cluster.", +"message.error.size.for.cluster": "Please enter size for Kubernetes Cluster.", "message.error.smb.password": "Please enter SMB password.", "message.error.smb.username": "Please enter SMB username.", "message.error.specify.stickiness.method": "Please specify a stickiness method", @@ -3192,7 +3454,7 @@ "message.error.upload.template": "Template upload failed.", "message.error.upload.template.description": "Only one Template can be uploaded at a time.", "message.error.url": "Please enter URL.", -"message.error.userdata": "Please enter Userdata", +"message.error.user.data": "Please enter the User Data", "message.error.username": "Enter your username.", "message.error.valid.iops.range": "Please enter a valid IOPS range.", "message.error.vcenter.datacenter": "Please enter vCenter datacenter.", @@ -3200,16 +3462,16 @@ "message.error.vcenter.host": "Please enter vCenter host.", "message.error.vcenter.password": "Please enter vCenter password.", "message.error.vcenter.username": "Please enter vCenter username.", -"message.error.version.for.cluster": "Please select Kubernetes version for Kubernetes cluster.", +"message.error.version.for.cluster": "Please select Kubernetes version for Kubernetes Cluster.", "message.error.vlan.range": "Please enter a valid VLAN/VNI range.", "message.error.volume.name": "Please enter volume name.", "message.error.volume": "Please enter volume.", "message.error.volume.group": "Please enter volume group.", -"message.error.zone": "Please select a zone.", -"message.error.zone.combined": "All zones cannot be combined with any other zone.", -"message.error.zone.for.cluster": "Please select zone for Kubernetes cluster.", -"message.error.zone.name": "Please enter zone name.", -"message.error.zone.type": "Please select zone type.", +"message.error.zone": "Please select a Zone.", +"message.error.zone.combined": "All Zones cannot be combined with any other zone.", +"message.error.zone.for.cluster": "Please select Zone for Kubernetes Cluster.", +"message.error.zone.name": "Please enter Zone name.", +"message.error.zone.type": "Please select Zone type.", "message.error.linstor.resourcegroup": "Please enter the Linstor Resource-Group.", "message.error.fixed.offering.kvm": "It's not possible to scale up Instances that utilize KVM hypervisor with a fixed compute offering.", "message.error.create.webhook.local.account": "Account must be provided for creating a Webhook with Local scope.", @@ -3229,39 +3491,49 @@ "message.host.controlstate.retry": "Some actions on this Instance will fail, if so please wait a while and retry.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released.", +"message.host.external.datadisk": "Usage of data disks for the selected template is not applicable", "message.import.running.instance.warning": "The selected VM is powered-on on the VMware Datacenter. The recommended state to convert a VMware VM into KVM is powered-off after a graceful shutdown of the guest OS.", "message.import.volume": "Please specify the domain, account or project name.
If not set, the volume will be imported for the caller.", "message.info.cloudian.console": "Cloudian Management Console should open in another window.", "message.installwizard.cloudstack.helptext.website": " * Project website:\t ", -"message.infra.setup.nsx.description": "This zone must contain an NSX provider because the isolation method is NSX", -"message.infra.setup.tungsten.description": "This zone must contain a Tungsten-Fabric provider because the isolation method is TF", +"message.infra.setup.netris.description": "This zone must contain a Netris provider because the isolation method is Netris", +"message.infra.setup.nsx.description": "This Zone must contain an NSX provider because the isolation method is NSX", +"message.infra.setup.tungsten.description": "This Zone must contain a Tungsten-Fabric provider because the isolation method is TF", "message.installwizard.cloudstack.helptext.document": " * Documentation:\t ", "message.installwizard.cloudstack.helptext.header": "\nYou can find more information about Apache CloudStack™ on the pages listed below.\n", "message.installwizard.cloudstack.helptext.issues": " * Report issues:\t ", "message.installwizard.cloudstack.helptext.mailinglists": " * Join mailing lists:\t ", "message.installwizard.cloudstack.helptext.releasenotes": " * Release notes:\t ", "message.installwizard.cloudstack.helptext.survey": " * Take the survey:\t ", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the Network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.\n\nExtending beyond individual Instance images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features.", -"message.installwizard.tooltip.addpod.name": "A name for the pod.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the Network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.\n\nExtending beyond individual Instance images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications.", +"message.installwizard.tooltip.addpod.name": "A name for the Pod.", "message.installwizard.tooltip.addpod.reservedsystemendip": "This is the IP range in the private Network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers.", -"message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway for the hosts in that pod.", +"message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway for the hosts in that Pod.", "message.installwizard.tooltip.addpod.reservedsystemstartip": "This is the IP range in the private Network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers.", -"message.installwizard.tooltip.configureguesttraffic.guestendip": "The range of IP addresses that will be available for allocation to guests in this zone. If one NIC is used, these IPs should be in the same CIDR as the pod CIDR.", +"message.installwizard.tooltip.configureguesttraffic.guestendip": "The range of IP addresses that will be available for allocation to guests in this Zone. If one NIC is used, these IPs should be in the same CIDR as the Pod CIDR.", "message.installwizard.tooltip.configureguesttraffic.guestgateway": "The gateway that the guests should use.", "message.installwizard.tooltip.configureguesttraffic.guestnetmask": "The netmask in use on the subnet that the guests should use.", -"message.installwizard.tooltip.configureguesttraffic.gueststartip": "The range of IP addresses that will be available for allocation to guests in this zone. If one NIC is used, these IPs should be in the same CIDR as the pod CIDR.", +"message.installwizard.tooltip.configureguesttraffic.gueststartip": "The range of IP addresses that will be available for allocation to guests in this Zone. If one NIC is used, these IPs should be in the same CIDR as the Pod CIDR.", +"message.installwizard.tooltip.netris.provider.name": "Netris Provider name is required", +"message.installwizard.tooltip.netris.provider.url": "Netris Provider URL not provided", +"message.installwizard.tooltip.netris.provider.username": "Netris Provider username not provided", +"message.installwizard.tooltip.netris.provider.password": "Netris Provider password not provided", +"message.installwizard.tooltip.netris.provider.site": "Netris Provider Site name not provided", +"message.installwizard.tooltip.netris.provider.tag": "Netris Tag to be assigned to vNets", +"message.installwizard.tooltip.netris.provider.tenant.name": "Netris Provider Admin Tenant name not provided", "message.installwizard.tooltip.nsx.provider.hostname": "NSX Provider hostname / IP address not provided", "message.installwizard.tooltip.nsx.provider.username": "NSX Provider username not provided", "message.installwizard.tooltip.nsx.provider.password": "NSX Provider password not provided", -"message.installwizard.tooltip.nsx.provider.edgecluster": "NSX Provider edge cluster information not provided", +"message.installwizard.tooltip.nsx.provider.edgecluster": "NSX Provider edge Cluster information not provided", "message.installwizard.tooltip.nsx.provider.tier0gateway": "NSX Provider tier-0 gateway information not provided", -"message.installwizard.tooltip.nsx.provider.transportZone": "NSX Provider transport zone information not provided", +"message.installwizard.tooltip.nsx.provider.transportZone": "NSX Provider transport Zone information not provided", "message.installwizard.tooltip.tungsten.provider.gateway": "Tungsten provider gateway is required", "message.installwizard.tooltip.tungsten.provider.hostname": "Tungsten provider hostname is required", "message.installwizard.tooltip.tungsten.provider.introspectport": "Tungsten provider introspect port is required", "message.installwizard.tooltip.tungsten.provider.name": "Tungsten provider name is required", "message.installwizard.tooltip.tungsten.provider.port": "Tungsten provider port is required", "message.installwizard.tooltip.tungsten.provider.vrouterport": "Tungsten provider vrouter port is required", +"message.instance.architecture": "Please select Instance architecture", "message.instances.managed": "Instances controlled by CloudStack.", "message.instances.unmanaged": "Instances not controlled by CloudStack.", "message.instances.migrate.vmware": "Instances that can be migrated from VMware.", @@ -3271,21 +3543,23 @@ "message.ip.v6.prefix.delete": "IPv6 prefix deleted", "message.iso.arch": "Please select an ISO architecture", "message.iso.desc": "Disc image containing data or bootable media for OS.", -"message.kubeconfig.cluster.not.available": "Kubernetes cluster kubeconfig not available currently.", -"message.kubernetes.cluster.delete": "Please confirm that you want to destroy the cluster.", -"message.kubernetes.cluster.scale": "Please select desired cluster configuration.", -"message.kubernetes.cluster.start": "Please confirm that you want to start the cluster.", -"message.kubernetes.cluster.stop": "Please confirm that you want to stop the cluster.", +"message.kubernetes.cluster.add.nodes": "Please confirm that you want to add the following nodes to the cluster", +"message.kubernetes.cluster.delete": "Please confirm that you want to destroy the Cluster.", +"message.kubeconfig.cluster.not.available": "Kubernetes Cluster kubeconfig not available currently.", +"message.kubernetes.cluster.remove.nodes": "Please confirm that you want to remove the following nodes from the cluster", +"message.kubernetes.cluster.scale": "Please select desired Cluster configuration.", +"message.kubernetes.cluster.start": "Please confirm that you want to start the Cluster.", +"message.kubernetes.cluster.stop": "Please confirm that you want to stop the Cluster.", "message.kubernetes.cluster.upgrade": "Please select new Kubernetes version.", "message.kubernetes.version.delete": "Please confirm that you want to delete this Kubernetes version.", -"message.l2.network.unsupported.for.nsx": "L2 networks aren't supported for NSX enabled zones", +"message.l2.network.unsupported.for.nsx": "L2 networks aren't supported for NSX enabled Zones", "message.launch.zone": "Zone is ready to launch; please proceed to the next step.", "message.launch.zone.description": "Zone is ready to launch; please proceed to the next step.", "message.launch.zone.hint": "Configure Network components and traffic including IP addresses.", "message.license.agreements.not.accepted": "License agreements not accepted.", "message.linstor.resourcegroup.description": "Linstor resource group to use for primary storage.", "message.list.zone.vmware.datacenter.empty": "No VMware Datacenter exists in the selected Zone", -"message.list.zone.vmware.hosts.empty": "No VMware hosts were found in the selected Datacenter", +"message.list.zone.vmware.hosts.empty": "No EXSi hosts were found in the selected Datacenter.\nAre the entered credentials correct?\n", "message.listnsp.not.return.providerid": "error: listNetworkServiceProviders API doesn't return VirtualRouter provider ID.", "message.load.host.failed": "Failed to load hosts.", "message.loadbalancer.stickypolicy.configuration": "Customize the load balancer stickiness policy:", @@ -3303,8 +3577,9 @@ "message.lock.user": "Please confirm that you want to lock the User \"{user}\". By locking this User, they will no longer be able to manage their cloud resources. Existing resources can still be accessed.", "message.lock.user.success": "Successfully locked User \"{user}\"", "message.login.failed": "Login Failed", -"message.migrate.instance.host.auto.assign": "Host for the Instance will be automatically chosen based on the suitability within the same cluster", -"message.migrate.instance.to.host": "Please confirm that you want to migrate this Instance to another host. When migration is between hosts of different clusters volume(s) of the Instance may get migrated to suitable storage pools.", +"message.maintenance.initiated": "Maintenance has been initiated. This Management Server will not accept new jobs", +"message.migrate.instance.host.auto.assign": "Host for the Instance will be automatically chosen based on the suitability within the same Cluster", +"message.migrate.instance.to.host": "Please confirm that you want to migrate this Instance to another host. When migration is between hosts of different Clusters volume(s) of the Instance may get migrated to suitable storage pools.", "message.migrate.instance.to.ps": "Please confirm that you want to migrate this Instance to another primary storage.", "message.migrate.resource.to.ss": "Please confirm that you want to migrate this resource to another secondary storage.", "message.migrate.router.confirm": "Please confirm the host you wish to migrate the router to:", @@ -3347,17 +3622,19 @@ "message.no.description": "No description entered.", "message.offering.internet.protocol.warning": "WARNING: IPv6 supported Networks use static routing and will require upstream routes to be configured manually.", "message.offering.ipv6.warning": "Please refer documentation for creating IPv6 enabled Network/VPC offering IPv6 support in CloudStack - Isolated Networks and VPC Network Tiers", +"message.oobm.configured": "Successfully configured out-of-band management for host", "message.ovf.configurations": "OVF configurations available for the selected appliance. Please select the desired value. Incompatible compute offerings will get disabled.", "message.password.reset.failed": "Failed to reset password.", "message.password.reset.success": "Password has been reset successfully. Please login using your new credentials.", "message.path": "Path : ", "message.path.description": "NFS: exported path from the server. VMFS: /datacenter name/datastore name. SharedMountPoint: path where primary storage is mounted, such as /mnt/primary.", +"message.please.confirm.remove.cni.configuration": "Please confirm that you want to remove this CNI Configuration", "message.please.confirm.remove.ssh.key.pair": "Please confirm that you want to remove this SSH key pair.", -"message.please.confirm.remove.user.data": "Please confirm that you want to remove this Userdata", +"message.please.confirm.remove.user.data": "Please confirm that you want to remove this User Data", "message.please.enter.valid.value": "Please enter a valid value.", "message.please.enter.value": "Please enter values.", "message.please.wait.while.autoscale.vmgroup.is.being.created": "Please wait while your AutoScaling Group is being created; this may take a while...", -"message.please.wait.while.zone.is.being.created": "Please wait while your zone is being created; this may take a while...", +"message.please.wait.while.zone.is.being.created": "Please wait while your Zone is being created; this may take a while...", "message.pod.dedicated": "Pod dedicated.", "message.pod.dedication.released": "Pod dedication released.", "message.prepare.for.shutdown": "Please confirm that you would like to prepare this Management Server for shutdown. It will not accept any new Async Jobs but will NOT terminate after there are no pending jobs.", @@ -3374,10 +3651,10 @@ "message.recover.vm": "Please confirm that you would like to recover this Instance.", "message.reinstall.vm": "NOTE: Proceed with caution. This will cause the Instance to be reinstalled from the Template; data on the root disk will be lost. Extra data volumes, if any, will not be touched.", "message.release.ip.failed": "Failed to release IP", -"message.releasing.dedicated.cluster": "Releasing dedicated cluster...", +"message.releasing.dedicated.cluster": "Releasing dedicated Cluster...", "message.releasing.dedicated.host": "Releasing dedicated host...", -"message.releasing.dedicated.pod": "Releasing dedicated pod...", -"message.releasing.dedicated.zone": "Releasing dedicated zone...", +"message.releasing.dedicated.pod": "Releasing dedicated Pod...", +"message.releasing.dedicated.zone": "Releasing dedicated Zone...", "message.remove.annotation": "Are you sure you want to delete the comment?", "message.remove.egress.rule.failed": "Removing egress rule failed", "message.remove.egress.rule.processing": "Deleting egress rule...", @@ -3414,6 +3691,7 @@ "message.restart.vm.to.update.settings": "Update in fields other than name and display name will require the Instance to be restarted.", "message.restart.vpc": "Please confirm that you want to restart the VPC.", "message.restart.vpc.remark": "Please confirm that you want to restart the VPC

Remark: making a non-redundant VPC redundant will force a clean up. The Networks will not be available for a couple of minutes.

", +"message.running.custom.action": "Running action", "message.scale.processing": "Scale in progress", "message.scaledown.policies": "Please add at least a ScaleDown policy. The AutoScale Group will be scaled down when all conditions in a ScaleDown policy are matched. ScaleDown policies will be checked after ScaleUp policies.", "message.scaledown.policy.continue": "Please add at least condition to ScaleDown policy to continue", @@ -3423,7 +3701,7 @@ "message.scaleup.policy.continue": "Please add at least a condition to ScaleUp policy to continue", "message.scaleup.policy.duration.continue": "Please input a valid duration to ScaleUp policy to continue", "message.scaleup.policy.name.continue": "Please input a name to ScaleUp policy to continue", -"message.select.a.zone": "A zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy.", +"message.select.a.zone": "A Zone typically corresponds to a single datacenter. Multiple zones help make the cloud more reliable by providing physical isolation and redundancy.", "message.select.affinity.groups": "Please select any affinity groups you want this Instance to belong to:", "message.select.bgp.peers": "Please select / deselect the BGP peers associated to the network or VPC:", "message.select.deselect.desired.options": "Please select / deselect the desired options", @@ -3431,29 +3709,29 @@ "message.select.destination.image.stores": "Please select Image Store(s) to which data is to be migrated to", "message.select.disk.offering": "Please select a disk offering for disk", "message.select.end.date.and.time": "Select an end date & time.", -"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the zone to perform the instance conversion through virt-v2v", -"message.select.kvm.host.instance.import": "(Optional) Select a KVM host in the cluster to perform the importing of the converted instance", +"message.select.kvm.host.instance.conversion": "(Optional) Select a KVM host in the Zone to perform the instance conversion through virt-v2v", +"message.select.kvm.host.instance.import": "(Optional) Select a KVM host in the Cluster to perform the importing of the converted instance", "message.select.load.balancer.rule": "Please select a load balancer rule for your AutoScale Instance group.", "message.select.migration.policy": "Please select a migration policy.", "message.select.nic.network": "Please select a Network for NIC", "message.select.security.groups": "Please select security group(s) for your new Instance.", "message.select.start.date.and.time": "Select a start date & time.", "message.select.temporary.storage.instance.conversion": "(Optional) Select a Storage temporary destination for the converted disks through virt-v2v", -"message.select.zone.description": "Select type of zone basic/advanced.", -"message.select.zone.hint": "This is the type of zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.", +"message.select.zone.description": "Select type of Zone basic/advanced.", +"message.select.zone.hint": "This is the type of Zone deployment that you want to use. Basic zone: provides a single Network where each Instance is assigned an IP directly from the Network. Guest isolation can be provided through layer-3 means such as security groups (IP address source filtering). Advanced zone: For more sophisticated Network topologies. This Network model provides the most flexibility in defining guest Networks and providing custom Network offerings such as firewall, VPN, or load balancer support.", "message.server": "Server : ", "message.server.description": "NFS, iSCSI, or PreSetup: IP address or DNS name of the storage device. VMWare PreSetup: IP address or DNS name of the vCenter server. Linstor: http(s) url of the linstor-controller.", "message.set.default.nic": "Please confirm that you would like to make this NIC the default for this Instance.", "message.set.default.nic.manual": "Please manually update the default NIC on the Instance now.", "message.setting.updated": "Setting Updated:", "message.setting.update.delay": "The new value will take effect within 30 seconds.", -"message.setup.physical.network.during.zone.creation": "When adding a zone, you need to set up one or more physical networks. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", -"message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical Network, which corresponds to a NIC on the hypervisor. The Network carries several types of traffic.

You may also add other traffic types onto the physical Network.", +"message.setup.physical.network.during.zone.creation": "When adding a Zone, you need to set up one or more physical networks. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", +"message.setup.physical.network.during.zone.creation.basic": "When adding a basic Zone, you can set up one physical Network, which corresponds to a NIC on the hypervisor. The Network carries several types of traffic.

You may also add other traffic types onto the physical Network.", "message.shared.network.offering.warning": "Domain admins and regular Users can only create shared Networks from Network offering with the setting specifyvlan=false. Please contact an administrator to create a Network offering if this list is empty.", -"message.shared.network.unsupported.for.nsx": "Shared networks aren't supported for NSX enabled zones", -"message.shutdown.triggered": "Shutdown has been triggered. This Management Server will not accept new jobs", -"message.maintenance.initiated": "Maintenance has been initiated. This Management Server will not accept new jobs", -"message.snapshot.additional.zones": "Snapshots will always be created in its native zone - %x, here you can select additional zone(s) where it will be copied to at creation time", +"message.shared.network.unsupported.for.nsx": "Shared networks aren't supported for NSX enabled Zones", +"message.shutdown.triggered": "A shutdown has been triggered. CloudStack will not accept new jobs", +"message.snapshot.additional.zones": "Snapshots will always be created in its native Zone - %x, here you can select additional zone(s) where it will be copied to at creation time", +"message.snapshot.desc": "Snapshot to create a ROOT disk from", "message.sourcenatip.change.warning": "WARNING: Changing the sourcenat IP address of the network will cause connectivity downtime for the Instances with NICs in the Network.", "message.sourcenatip.change.inhibited": "Changing the sourcenat to this IP of the Network to this address is inhibited as firewall rules are defined for it. This can include port forwarding or load balancing rules.\n - If this is an Isolated Network, please use updateNetwork/click the edit button.\n - If this is a VPC, first clear all other rules for this address.", "message.specify.tag.key": "Please specify a tag key.", @@ -3467,6 +3745,7 @@ "message.success.add.egress.rule": "Successfully added new egress rule", "message.success.add.firewall.rule": "Successfully added new firewall rule", "message.success.add.guest.network": "Successfully created guest Network", +"message.success.add.gpu.device": "Successfully added GPU device", "message.success.add.interface.static.route": "Successfully added interface Static Route", "message.success.add.iprange": "Successfully added IP range", "message.success.add.ipv4.subnet": "Successfully added IPv4 subnet", @@ -3475,9 +3754,11 @@ "message.success.add.kuberversion": "Successfully added Kubernetes version", "message.success.add.logical.router": "Successfully added Logical Router", "message.success.add.network": "Successfully added Network", -"message.success.add.network.acl": "Successfully added Network ACL list", +"message.success.add.network.acl": "Successfully added Network ACL", "message.success.add.network.static.route": "Successfully added Network Static Route", "message.success.add.network.permissions": "Successfully added Network permissions", +"message.success.add.nodes.to.cluster": "Successfully added nodes to Kubernetes cluster", +"message.success.remove.nodes.from.cluster": "Successfully removed nodes from Kubernetes cluster", "message.success.add.physical.network": "Successfully added Physical Network", "message.success.add.object.storage": "Successfully added Object Storage", "message.success.add.policy.rule": "Successfully added Policy rule", @@ -3494,6 +3775,7 @@ "message.success.add.vpc.network": "Successfully added a VPC network", "message.success.add.vpn.customer.gateway": "Successfully added VPN customer gateway", "message.success.add.vpn.gateway": "Successfully added VPN gateway", +"message.success.assign.sslcert": "Successfully assigned SSL certificate", "message.success.assign.vm": "Successfully assigned Instance", "message.success.apply.network.policy": "Successfully applied Network Policy", "message.success.apply.tungsten.tag": "Successfully applied Tag", @@ -3504,6 +3786,7 @@ "message.success.change.bgp.peers": "Successfully changed BGP peers", "message.success.change.offering": "Successfully changed offering", "message.success.change.password": "Successfully changed password for User", +"message.success.change.host.password": "Successfully changed password for host \"{name}\"", "message.success.clear.webhook.deliveries": "Successfully cleared webhook deliveries", "message.success.change.scope": "Successfully changed scope for storage pool", "message.success.config.backup.schedule": "Successfully configured Instance backup schedule", @@ -3514,11 +3797,12 @@ "message.success.create.account": "Successfully created Account", "message.success.create.asnrange": "Successfully created AS Range", "message.success.create.bucket": "Successfully created bucket", +"message.success.create.extension": "Successfully created Extension", "message.success.create.sharedfs": "Successfully created Shared FileSystem", "message.success.create.internallb": "Successfully created Internal Load Balancer", "message.success.create.isolated.network": "Successfully created isolated Network", "message.success.create.keypair": "Successfully created SSH key pair", -"message.success.create.kubernetes.cluter": "Successfully created Kubernetes cluster", +"message.success.create.kubernetes.cluter": "Successfully created Kubernetes Cluster", "message.success.create.l2.network": "Successfully created L2 Network", "message.success.create.snapshot.from.vmsnapshot": "Successfully created Snapshot from Instance Snapshot", "message.success.create.template": "Successfully created Template", @@ -3532,6 +3816,8 @@ "message.success.delete.acl.rule": "Successfully removed ACL rule", "message.success.delete.backup.schedule": "Successfully deleted configure Instance backup schedule", "message.success.delete.bgp.peer": "Successfully deleted BGP peer", +"message.success.delete.custom.action": "Successfully deleted Custom Action", +"message.success.delete.gpu.devices": "Successfully deleted GPU device(s)", "message.success.delete.icon": "Successfully deleted icon of", "message.success.delete.interface.static.route": "Successfully removed interface Static Route", "message.success.delete.ipv4.subnet": "Successfully removed IPv4 subnet", @@ -3544,26 +3830,31 @@ "message.success.delete.tungsten.router.table": "Successfully removed Router Table", "message.success.delete.tungsten.tag": "Successfully removed Tag", "message.success.delete.vm": "Successfully deleted Instance", +"message.success.delete.vpn.gateway": "Successfully deleted VPN gateway", "message.success.disable.saml.auth": "Successfully disabled SAML authorization", "message.success.disable.vpn": "Successfully disabled VPN", +"message.success.discover.gpu.devices": "Successfully discovered GPU devices", "message.success.edit.acl": "Successfully edited ACL rule", "message.success.edit.primary.storage": "Successfully edited Primary Storage", "message.success.edit.rule": "Successfully edited rule", "message.success.enable.saml.auth": "Successfully enabled SAML Authorization", "message.success.import.instance": "Successfully imported Instance", "message.success.import.volume": "Successfully imported Volume", +"message.success.manage.gpu.devices": "Successfully managed GPU device(s)", "message.success.migrate.volume": "Successfully migrated volume", "message.success.migrating": "Migration completed successfully for", "message.success.migration": "Migration completed successfully", "message.success.move.acl.order": "Successfully moved ACL rule", "message.success.recurring.snapshot": "Successfully recurring Snapshots", +"message.success.register.extension": "Successfully registered Extension", "message.success.register.iso": "Successfully registered ISO", "message.success.register.keypair": "Successfully registered SSH key pair", "message.success.register.template": "Successfully registered Template", -"message.success.register.user.data": "Successfully registered Userdata", +"message.success.register.user.data": "Successfully registered User Data", "message.success.release.ip": "Successfully released IP", "message.success.release.dedicated.bgp.peer": "Successfully released dedicated BGP peer", "message.success.release.dedicated.ipv4.subnet": "Successfully released dedicated IPv4 subnet", +"message.success.remove.sslcert": "Successfully removed SSL certificate from load balancer", "message.success.remove.egress.rule": "Successfully removed egress rule", "message.success.remove.objectstore.objects": "Successfully removed selected object(s)", "message.success.remove.objectstore.directory": "Successfully removed selected directory", @@ -3583,26 +3874,36 @@ "message.success.remove.tungsten.routing.policy": "Successfully removed Tungsten-Fabric Routing Policy from Network", "message.success.reset.network.permissions": "Successfully reset Network Permissions", "message.success.resize.volume": "Successfully resized volume", -"message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster", +"message.success.scale.kubernetes": "Successfully scaled Kubernetes Cluster", +"message.success.unmanage.gpu.devices": "Successfully unmanaged GPU device(s)", "message.success.unmanage.instance": "Successfully unmanaged Instance", "message.success.unmanage.volume": "Successfully unmanaged Volume", +"message.success.unregister.extension": "Successfull unregistered Extension", "message.success.update.account": "Successfully updated Account", "message.success.update.bgp.peer": "Successfully updated BGP peer", "message.success.update.bucket": "Successfully updated bucket", "message.success.update.condition": "Successfully updated condition", +"message.success.update.gpu.device": "Successfully updated GPU device", +"message.success.create.vgpu.profile": "Successfully created vGPU profile", +"message.success.update.vgpu.profile": "Successfully updated vGPU profile", +"message.success.delete.vgpu.profile": "Successfully deleted vGPU profile", +"message.success.update.custom.action": "Successfully updated Custom Action", +"message.success.update.extension": "Successfully updated Extension", "message.success.update.sharedfs": "Successfully updated Shared FileSystem", "message.success.update.ipaddress": "Successfully updated IP address", "message.success.update.iprange": "Successfully updated IP range", "message.success.update.ipv4.subnet": "Successfully updated IPv4 subnet", +"message.success.update.iso": "Successfully updated ISO", "message.success.update.kubeversion": "Successfully updated Kubernetes supported version", "message.success.update.network": "Successfully updated Network", "message.success.update.template": "Successfully updated Template", "message.success.update.user": "Successfully updated User", -"message.success.upgrade.kubernetes": "Successfully upgraded Kubernetes cluster", +"message.success.upgrade.kubernetes": "Successfully upgraded Kubernetes Cluster", "message.success.upload": "Successfully uploaded", "message.success.upload.description": "This ISO file has been uploaded. Please check its status in the Templates menu.", "message.success.upload.icon": "Successfully uploaded icon for ", "message.success.upload.iso.description": "This ISO file has been uploaded. Please check its status in the images > ISOs menu.", +"message.success.upload.ssl.cert": "Successfully uploaded SSL certificate", "message.success.upload.template.description": "This Template file has been uploaded. Please check its status in the Templates menu.", "message.success.upload.volume.description": "This volume has been uploaded. Please check its status in the volumes menu.", "message.suspend.project": "Are you sure you want to suspend this project?", @@ -3611,12 +3912,12 @@ "message.template.arch": "Please select a Template architecture.", "message.template.desc": "OS image that can be used to boot Instances.", "message.template.import.vm.temporary": "If a temporary Template is used, the reset Instance operation will not work after importing it.", -"message.template.iso": "Please select a Template or ISO to continue.", +"message.template.iso": "Please select a Template, ISO, volume or a snapshot to continue.", "message.template.type.change.warning": "WARNING: Changing the Template type to SYSTEM will disable further changes to the Template.", -"message.tooltip.reserved.system.netmask": "The Network prefix that defines the pod subnet. Uses CIDR notation.", +"message.tooltip.reserved.system.netmask": "The Network prefix that defines the Pod subnet. Uses CIDR notation.", "message.traffic.type.deleted": "Successfully deleted traffic type", -"message.traffic.type.to.basic.zone": "traffic type to basic zone", -"message.trigger.shutdown": "Please confirm that you would like to trigger a shutdown on this Management Server. It will not accept any new Async Jobs and will terminate after there are no pending jobs.", +"message.traffic.type.to.basic.zone": "traffic type to basic Zone", +"message.trigger.shutdown": "Please confirm that you would like to trigger a shutdown on this Management server. It will not accept any new Async Jobs and will terminate after there are no pending jobs.", "message.type.values.to.add": "Please add additional values by typing them in", "message.update.autoscale.policy.failed": "Failed to update autoscale policy", "message.update.autoscale.vmgroup.failed": "Failed to update autoscale group", @@ -3686,6 +3987,7 @@ "message.vnf.nic.move.down.fail": "Failed to move down this NIC", "message.vnf.no.credentials": "No credentials found for the VNF appliance.", "message.vnf.select.networks": "Please select the relevant network for each VNF NIC.", +"message.volume.desc": "Volume to use as a ROOT disk", "message.volume.state.allocated": "The volume is allocated but has not been created yet.", "message.volume.state.attaching": "The volume is attaching to a volume from Ready state.", "message.volume.state.copying": "The volume is being copied from the image store to primary storage, in case it's an uploaded volume.", @@ -3709,18 +4011,19 @@ "message.volumes.managed": "Volumes controlled by CloudStack.", "message.volumes.unmanaged": "Volumes not controlled by CloudStack.", "message.vpc.restart.required": "Restart is required for VPC(s). Click here to view VPC(s) which require restart.", -"message.vr.alert.upon.network.offering.creation.l2": "As virtual routers are not created for L2 Networks, the compute offering will not be used.", "message.vr.alert.upon.network.offering.creation.others": "As none of the obligatory services for creating a virtual router (VPN, DHCP, DNS, Firewall, LB, UserData, SourceNat, StaticNat, PortForwarding) are enabled, the virtual router will not be created and the compute offering will not be used.", "message.warn.change.primary.storage.scope": "This feature is tested and supported for the following configurations:
KVM - NFS/Ceph - DefaultPrimary
VMware - NFS - DefaultPrimary
*There might be extra steps involved to make it work for other configurations.", "message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats.", "message.warn.importing.instance.without.nic": "WARNING: This Instance is being imported without NICs and many Network resources will not be available. Consider creating a NIC via vCenter before importing or as soon as the Instance is imported.", -"message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network’s MTU settings", +"message.warn.select.template": "Please select a Template for Registration.", +"message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network's MTU settings", "message.webhook.deliveries.time.filter": "Webhook deliveries list can be filtered based on date-time. Select 'Custom' for specifying start and end date range.", "message.zone.creation.complete": "Zone creation complete.", -"message.zone.detail.description": "Populate zone details.", -"message.zone.detail.hint": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", +"message.zone.detail.description": "Populate Zone details.", +"message.zone.detail.hint": "A Zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more Pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.", "message.validate.min": "Please enter a value greater than or equal to {0}.", "message.action.delete.object.storage": "Please confirm that you want to delete this Object Store", +"message.action.unregister.extension.resource": "Please confirm that you want to unregister extension with this resource", "message.bgp.peers.null": "Please note, if no BGP peers are selected, the VR will connect to
(1) dedicated BGP peers the owner can access, if the owner has dedicated BGP peers and account setting use.system.bgp.peers is set to false;
(2) all BGP peers the owner can access, otherwise.
", "message.bucket.delete": "Please confirm that you want to delete this Bucket", "migrate.from": "Migrate from", @@ -3754,6 +4057,7 @@ "state.stopped": "Stopped", "state.stopping": "Stopping", "state.suspended": "Suspended", +"state.partiallyallocated": "Partially Allocated", "user.login": "Login", "user.logout": "Logout", "ALLOCATED_VM": "Allocated VM", diff --git a/ui/public/locales/es.json b/ui/public/locales/es.json index 76488937574..ab57c951531 100644 --- a/ui/public/locales/es.json +++ b/ui/public/locales/es.json @@ -14,12 +14,12 @@ "label.account.specific": "espec\u00edficas de la cuenta", "label.accounts": "Cuentas", "label.accounttype": "Tipo de Cuenta", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ID de ACL", -"label.acl.list.rules": "Lista de Reglas ACL", +"label.acl.rules": "Lista de Reglas ACL", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "Nombre de ACL", +"label.acl.rule.name": "Nombre de ACL", "label.acquire.new.ip": "Adquirir nueva IP", "label.acquire.new.secondary.ip": "Adquirir nueva IP secundaria", "label.action": "Acci\u00f3n", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Sesiones activas", "label.add": "Agregar", "label.add.account": "A\u00f1adir Cuenta", -"label.add.acl": "Agregar ACL", -"label.add.acl.list": "Agregar Lista ACL", +"label.add.acl.rule": "Agregar ACL", +"label.add.acl": "Agregar Lista ACL", "label.add.affinity.group": "Agregar un nuevo grupo de afinidad", "label.add.baremetal.dhcp.device": "Agregar dispositivo DHCP Baremetal", "label.add.bigswitchbcf.device": "Agregar Controlador BigSwitch BCF", @@ -142,12 +142,12 @@ "label.add.ip.range": "A\u00f1adir Rango IP", "label.add.isolated.network": "Agregar Red Aislada", "label.add.ldap.account": "Agregar cuenta LDAP", -"label.add.list.name": "Nombre de la Lista ACL", +"label.add.acl.name": "Nombre de la Lista ACL", "label.add.more": "A\u00f1adir m\u00e1s", "label.add.netscaler.device": "Agregar dispositivo Netscaler", "label.add.network": "Agregar Red", "label.add.network.acl": "Agregar ACL de Red", -"label.add.network.acl.list": "Agregar Lista ACL de Red", +"label.add.network.acl": "Agregar Lista ACL de Red", "label.add.network.offering": "Agregar Oferta de Red", "label.add.new.gateway": "Agregar nuevo gateway", "label.add.new.tier": "Agregar un nuevo tier", @@ -344,7 +344,7 @@ "label.default.use": "Uso por defecto", "label.default.view": "Vista Por Defecto", "label.delete": "Eliminar", -"label.delete.acl.list": "Borrar Lista ACL", +"label.delete.acl": "Borrar Lista ACL", "label.delete.affinity.group": "Borrar Grupo de Afinidad", "label.delete.alerts": "Eliminar alertas", "label.delete.bigswitchbcf": "Remover Controlador BigSwitch BCF", @@ -431,7 +431,7 @@ "label.driver": "Controlador", "label.dynamicscalingenabled": "Escalado din\u00e1mico habilitado", "label.edit": "Editar", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Editar regla ACL", "label.edit.project.details": "Editar detalles de proyecto", "label.edit.role": "Editar Rol", @@ -947,7 +947,6 @@ "label.remove.vpc.offering": "Quitar Oferta VPC", "label.removing": "Quitando..", "label.replace.acl": "Reemplazar ACL", -"label.replace.acl.list": "Reemplazar Lista ACL", "label.report.bug": "Reportar un Error", "label.required": "Requerido", "label.requireshvm": "HVM", @@ -1180,8 +1179,7 @@ "label.usehttps": "Use HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Usuario", -"label.userdata": "DatosUsuario", -"label.userdatal2": "Datos de Usuario", +"label.user.data": "DatosUsuario", "label.username": "Nombre de usuario", "label.users": "Usuarios", "label.utilization": "Utilisation", @@ -1361,7 +1359,7 @@ "message.confirm.archive.selected.alerts": "Por favor confirme que desea archivar las alertas seleccionadas", "message.confirm.archive.selected.events": "Por favor confirme que desea archivar los eventos seleccionados", "message.confirm.attach.disk": "\u00bf Est\u00e1 seguro que desea conectar el disco?", -"message.confirm.delete.acl.list": "\u00bfEsta seguro que desea borrar esta lista de ACL?", +"message.confirm.delete.acl": "\u00bfEsta seguro que desea borrar esta lista de ACL?", "message.confirm.delete.bigswitchbcf": "Por favor confirme que desa borrar este Controlador BigSwitch BCF", "message.confirm.delete.brocadevcs": "Por favor confirme que desa borrar este Switch Brocade Vcs", "message.confirm.delete.ciscoasa1000v": "Por favor confirme que desea borrar CiscoASA1000v", @@ -1437,7 +1435,7 @@ "message.guest.traffic.in.basic.zone": "El tr\u00e1fico de las redes invitado es el generado entre las m\u00e1quina virtuales del usuario final. Especifique un rango de direcciones IP para que CloudStack pueda asignar a las MVs Invitado. Asegures\u00e9 que este rango no se solape con el rango IP reservado para el sistema.", "message.host.dedicated": "Servidor Dedicado", "message.host.dedication.released": "Dedicaci\u00f3n de Servidor liberada", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ es una plataforma de software que aglutina recursos c\u00f3mputo para construir Infraestructuras como Servicio (IaaS), tanto de cloud p\u00fablico como privado e h\u00edbrido.\nCloudStack™ gestiona la red, el almacenamiento y los nodos de c\u00f3mputo que conforma la infraestructura de cloud. Se puede usar CloudStack™ para desplegar, gestionar y configurar entornos de computaci\u00f3n en la nube.

Cloudstack™ vam\u00e1s all\u00e1 del manejo individual de m\u00e1quinas virtuales en hardware de prop\u00f3sito general, ya que proporciona una soluci\u00f3n llave en mano para desplegar datacenters como servicio - proporcionando todos los componentes esenciales para construir, desplegar y gestionar aplicaciones cloud multi-tier y multi-tenant. Se ofrecen dos versiones, la open source y la Premium, brindando la primera caracter\u00edsticas casi id\u00e9nticas.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ es una plataforma de software que aglutina recursos c\u00f3mputo para construir Infraestructuras como Servicio (IaaS), tanto de cloud p\u00fablico como privado e h\u00edbrido.\nCloudStack™ gestiona la red, el almacenamiento y los nodos de c\u00f3mputo que conforma la infraestructura de cloud. Se puede usar CloudStack™ para desplegar, gestionar y configurar entornos de computaci\u00f3n en la nube.

Cloudstack™ vam\u00e1s all\u00e1 del manejo individual de m\u00e1quinas virtuales en hardware de prop\u00f3sito general, ya que proporciona una soluci\u00f3n llave en mano para desplegar datacenters como servicio - proporcionando todos los componentes esenciales para construir, desplegar y gestionar aplicaciones cloud multi-tier y multi-tenant.", "message.installwizard.tooltip.addpod.name": "Nombre del POD", "message.installwizard.tooltip.addpod.reservedsystemendip": "Este es el rango de direcciones IP en la red privada que CloudStack utiliza para administrar las MVs del Almacenamiento Secundario y proxy de consolas. Estas direcciones IP se han tomado de la misma subred que los servidores inform\u00e1ticos.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "La puerta de enlace para los host en ese pod.", diff --git a/ui/public/locales/fr_FR.json b/ui/public/locales/fr_FR.json index f11f4ac5c94..424f4749aa3 100644 --- a/ui/public/locales/fr_FR.json +++ b/ui/public/locales/fr_FR.json @@ -14,12 +14,12 @@ "label.account.specific": "Sp\u00e9cifique au compte", "label.accounts": "Comptes", "label.accounttype": "Type Compte", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ID ACL", -"label.acl.list.rules": "Liste r\u00e8gles ACL", +"label.acl.rules": "Liste r\u00e8gles ACL", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "Nom ACL", +"label.acl.rule.name": "Nom ACL", "label.acquire.new.ip": "Acqu\u00e9rir nouvelle adr. IP", "label.acquire.new.secondary.ip": "Acqu\u00e9rir nouvelle IP secondaire", "label.action": "Action", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Sessions actives", "label.add": "Ajouter", "label.add.account": "Ajouter un compte", -"label.add.acl": "Ajouter r\u00e8gle ACL", -"label.add.acl.list": "Ajouter Liste ACL", +"label.add.acl.rule": "Ajouter r\u00e8gle ACL", +"label.add.acl": "Ajouter Liste ACL", "label.add.affinity.group": "Ajouter nouveau groupe d'affinit\u00e9", "label.add.baremetal.dhcp.device": "Ajouter un DHCP Baremetal", "label.add.bigswitchbcf.device": "Ajouter un contr\u00f4leur BigSwitch BCF", @@ -142,12 +142,12 @@ "label.add.ip.range": "Ajouter une plage IP", "label.add.isolated.network": "Ajouter un r\u00e9seau isol\u00e9", "label.add.ldap.account": "Ajouter un compte LDAP", -"label.add.list.name": "Nom Liste ACL", +"label.add.acl.name": "Nom Liste ACL", "label.add.more": "Ajouter plus", "label.add.netscaler.device": "Ajouter un Netscaler", "label.add.network": "Ajouter un r\u00e9seau", "label.add.network.acl": "Ajouter une r\u00e8gle d'acc\u00e8s r\u00e9seau ACL", -"label.add.network.acl.list": "Ajouter Liste ACL r\u00e9seau", +"label.add.network.acl": "Ajouter Liste ACL r\u00e9seau", "label.add.network.offering": "Ajouter Offre R\u00e9seau", "label.add.new.gateway": "Ajouter une nouvelle passerelle", "label.add.new.tier": "Ajouter un nouveau tiers", @@ -334,7 +334,7 @@ "label.default.use": "Utilisation par d\u00e9faut", "label.default.view": "Vue par d\u00e9faut", "label.delete": "Supprimer", -"label.delete.acl.list": "Supprimer Liste ACL", +"label.delete.acl": "Supprimer Liste ACL", "label.delete.affinity.group": "Supprimer le groupe d'affinit\u00e9", "label.delete.alerts": "Supprimer alertes", "label.delete.bigswitchbcf": "Supprimer contr\u00f4leur BigSwitch BCF", @@ -419,7 +419,7 @@ "label.dpd": "D\u00e9tection de pair mort", "label.driver": "Pilote", "label.edit": "Modifier", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Modifier r\u00e8gle ACL", "label.edit.project.details": "Modifier les d\u00e9tails du projet", "label.edit.role": "\u00c9diter R\u00f4le", @@ -926,7 +926,6 @@ "label.remove.vpc.offering": "Supprimer offre VPC", "label.removing": "Suppression", "label.replace.acl": "Remplacer ACL", -"label.replace.acl.list": "Remplacer Liste ACL", "label.required": "Requis", "label.requireshvm": "HVM", "label.requiresupgrade": "Mise \u00e0 jour n\u00e9cessaire", @@ -1152,8 +1151,7 @@ "label.usehttps": "Utiliser HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Utilisateur", -"label.userdata": "Donn\u00e9es Utilisateur", -"label.userdatal2": "Donn\u00e9es utilisateur", +"label.user.data": "Donn\u00e9es Utilisateur", "label.username": "Identifiant", "label.users": "Utilisateurs", "label.utilization": "Utilisation", @@ -1331,7 +1329,7 @@ "message.confirm.archive.selected.alerts": "Confirmer l'archivage des alertes s\u00e9lectionn\u00e9es", "message.confirm.archive.selected.events": "Confirmez l'archivage des \u00e9v\u00e9nements s\u00e9lectionn\u00e9s", "message.confirm.attach.disk": "Confirmer le rattachement de ce disque ?", -"message.confirm.delete.acl.list": "Confirmer la suppression de cette liste ACL ?", +"message.confirm.delete.acl": "Confirmer la suppression de cette liste ACL ?", "message.confirm.delete.bigswitchbcf": "Confirmer que vous voulez supprimer ce contr\u00f4leur BigSwitch BCF", "message.confirm.delete.brocadevcs": "Confirmer la suppression du switch Brocade Vcs", "message.confirm.delete.ciscoasa1000v": "Confirmez la suppression du CiscoASA1000v", @@ -1407,7 +1405,7 @@ "message.guest.traffic.in.basic.zone": "Le trafic r\u00e9seau d'invit\u00e9 est la communication entre les machines virtuelles utilisateur. Sp\u00e9cifier une plage d'adresses IP que CloudStack peut assigner aux machines virtuelles Invit\u00e9. S'assurer que cette plage n'empi\u00e8te pas sur la plage r\u00e9serv\u00e9e aux adresses IP Syst\u00e8me.", "message.host.dedicated": "H\u00f4te d\u00e9di\u00e9e", "message.host.dedication.released": "Lib\u00e9ration de l'h\u00f4te d\u00e9di\u00e9", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ est une plate-forme logicielle de pools de ressources informatiques pour construire des infrastructures publiques, priv\u00e9es et hybrides en tant que services (IaaS) dans les nuages. CloudStack™ g\u00e8re le r\u00e9seau, le stockage et les noeuds de calcul qui composent une infrastructure dans les nuages. Utilisez CloudStack™ pour d\u00e9ployer, g\u00e9rer et configurer les environnements d'informatiques dans les nuages.

S'\u00e9tendant au-del\u00e0 des machines virtuelles individuelles fonctionnant sur du mat\u00e9riel standard, CloudStack™ offre une solution d'informatique en nuage cl\u00e9 en main pour fournir des centres de donn\u00e9es virtuels comme service - fournissant tous les composants essentiels pour construire, d\u00e9ployer et g\u00e9rer des applications 'cloud' multi-niveaux et multi-locataire. Les versions libre et Premium sont disponibles, la version Libre offrant des caract\u00e9ristiques presque identiques.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ est une plate-forme logicielle de pools de ressources informatiques pour construire des infrastructures publiques, priv\u00e9es et hybrides en tant que services (IaaS) dans les nuages. CloudStack™ g\u00e8re le r\u00e9seau, le stockage et les noeuds de calcul qui composent une infrastructure dans les nuages. Utilisez CloudStack™ pour d\u00e9ployer, g\u00e9rer et configurer les environnements d'informatiques dans les nuages.

S'\u00e9tendant au-del\u00e0 des machines virtuelles individuelles fonctionnant sur du mat\u00e9riel standard, CloudStack™ offre une solution d'informatique en nuage cl\u00e9 en main pour fournir des centres de donn\u00e9es virtuels comme service - fournissant tous les composants essentiels pour construire, d\u00e9ployer et g\u00e9rer des applications 'cloud' multi-niveaux et multi-locataire.", "message.installwizard.tooltip.addpod.name": "Nom pour le pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Ceci est la plage d'adresses IP dans le r\u00e9seau priv\u00e9 que CloudStack utilise la gestion des VMs du stockage secondaire et les VMs Console Proxy. Ces adresses IP sont prises dans le m\u00eame sous-r\u00e9seau que les serveurs h\u00f4tes.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "Passerelle pour les serveurs dans ce pod", diff --git a/ui/public/locales/hi.json b/ui/public/locales/hi.json index 055909360b5..9d7440b9262 100644 --- a/ui/public/locales/hi.json +++ b/ui/public/locales/hi.json @@ -6,7 +6,7 @@ "label.accounts": "लेखा", "label.accounttype": "खाता प्रकार", "label.aclid": "ACL", -"label.aclname": "ACL नाम", +"label.acl.rule.name": "ACL नाम", "label.actions": "क्रियाएँ", "label.add": "जोड़ें", "label.adding": "जोड़ना", @@ -410,7 +410,7 @@ "label.usageunit": "Unit", "label.usehttps": "HTTPS का उपयोग करें", "label.user": "उपयोगकर्ता", -"label.userdata": "Userdata", +"label.user.data": "User Data", "label.username": "उपयोगकर्ता नाम", "label.users": "उपयोगकर्ता", "label.uuid": "ID", diff --git a/ui/public/locales/hu.json b/ui/public/locales/hu.json index 445d3a1d5f5..ae58379d343 100644 --- a/ui/public/locales/hu.json +++ b/ui/public/locales/hu.json @@ -14,12 +14,12 @@ "label.account.specific": "Sz\u00e1mla-specifikus", "label.accounts": "Sz\u00e1ml\u00e1k", "label.accounttype": "Sz\u00e1mla t\u00edpus", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL n\u00e9v", +"label.acl.rule.name": "ACL n\u00e9v", "label.acquire.new.ip": "\u00daj IP c\u00edm beszerz\u00e9se", "label.acquire.new.secondary.ip": "\u00daj m\u00e1sodlagos IP c\u00edm beszerz\u00e9se", "label.action": "M\u0171velet", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Akt\u00edv munkamenetek", "label.add": "Felv\u00e9tel", "label.add.account": "Sz\u00e1mla felv\u00e9tele", -"label.add.acl": "ACL felv\u00e9tele", -"label.add.acl.list": "ACL lista felv\u00e9tele", +"label.add.acl.rule": "ACL felv\u00e9tele", +"label.add.acl": "ACL Lista felv\u00e9tele", "label.add.affinity.group": "\u00daj affin\u00edt\u00e1si csoport felv\u00e9tele", "label.add.baremetal.dhcp.device": "Baremetal DHCP eszk\u00f6z felv\u00e9tele", "label.add.bigswitchbcf.device": "BigSwitch BCF vez\u00e9rl\u0151 felv\u00e9tele", @@ -142,12 +142,12 @@ "label.add.ip.range": "IP c\u00edmtartom\u00e1ny felv\u00e9tele", "label.add.isolated.network": "Izol\u00e1lt h\u00e1l\u00f3zat felv\u00e9tele", "label.add.ldap.account": "LDAP hozz\u00e1f\u00e9r\u00e9s felv\u00e9tele", -"label.add.list.name": "ACL lista n\u00e9v", +"label.add.acl.name": "ACL Lista n\u00e9v", "label.add.more": "Tov\u00e1bbi felv\u00e9tele", "label.add.netscaler.device": "Netscaler eszk\u00f6z felv\u00e9tele", "label.add.network": "H\u00e1l\u00f3zat felv\u00e9tele", "label.add.network.acl": "H\u00e1l\u00f3zati ACL felv\u00e9tele", -"label.add.network.acl.list": "H\u00e1l\u00f3zati ACL lista felv\u00e9tele", +"label.add.network.acl": "H\u00e1l\u00f3zati ACL Lista felv\u00e9tele", "label.add.network.offering": "H\u00e1l\u00f3zati aj\u00e1nlat felv\u00e9tele", "label.add.new.gateway": "\u00daj \u00e1tj\u00e1r\u00f3 felv\u00e9tele", "label.add.new.tier": "\u00daj r\u00e9teg felv\u00e9tele", @@ -334,7 +334,7 @@ "label.default.use": "Alap\u00e9rtelmezett haszn\u00e1lat", "label.default.view": "Alap\u00e9rtelmezett n\u00e9zet", "label.delete": "T\u00f6rl\u00e9s", -"label.delete.acl.list": "ACL lista t\u00f6rl\u00e9se", +"label.delete.acl": "ACL Lista t\u00f6rl\u00e9se", "label.delete.affinity.group": "Affin\u00edt\u00e1si csoport t\u00f6rl\u00e9se", "label.delete.alerts": "T\u00f6rl\u00e9s riaszt\u00e1sok", "label.delete.bigswitchbcf": "BigSwitch BCF vez\u00e9rl\u0151 elt\u00e1vol\u00edt\u00e1sa", @@ -419,7 +419,7 @@ "label.driver": "Driver", "label.dynamicscalingenabled": "dinamikus m\u00e9retez\u00e9s enged\u00e9lyezve", "label.edit": "Szerkeszt\u00e9s", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "ACL szab\u00e1ly szerkeszt\u00e9se", "label.edit.project.details": "Projekt r\u00e9szletek szerkeszt\u00e9se", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "VPC aj\u00e1nlat t\u00f6rl\u00e9se", "label.removing": "T\u00f6rl\u00e9s", "label.replace.acl": "ACL csere", -"label.replace.acl.list": "ACL lista cser\u00e9je", "label.required": "Sz\u00fcks\u00e9ges", "label.requireshvm": "HVM", "label.requiresupgrade": "Friss\u00edt\u00e9st ig\u00e9nyel", @@ -1150,8 +1149,7 @@ "label.usehttps": "HTTPS haszn\u00e1lata", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Felhaszn\u00e1l\u00f3", -"label.userdata": "Felhaszn\u00e1l\u00f3 adat", -"label.userdatal2": "Felhaszn\u00e1l\u00f3i adat", +"label.user.data": "Felhaszn\u00e1l\u00f3 adat", "label.username": "Felhaszn\u00e1l\u00f3n\u00e9v", "label.users": "Felhaszn\u00e1l\u00f3k", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Er\u0151s\u00edtsd meg, hogy le akarod archiv\u00e1lni a kiv\u00e1lasztott riaszt\u00e1sokat!", "message.confirm.archive.selected.events": "Er\u0151s\u00edtsd meg, hogy archiv\u00e1lni szeretn\u00e9d a kiv\u00e1lasztott esem\u00e9nyeket!", "message.confirm.attach.disk": "Biztosan csatolni szeretn\u00e9d a merevlemezt?", -"message.confirm.delete.acl.list": "Biztosan t\u00f6r\u00f6lni akarod ezt a ACL list\u00e1t?", +"message.confirm.delete.acl": "Biztosan t\u00f6r\u00f6lni akarod ezt a ACL List\u00e1t?", "message.confirm.delete.bigswitchbcf": "Er\u0151s\u00edtsd meg, hogy t\u00f6r\u00f6lni szeretn\u00e9d ezt a BigSwitch BCF vez\u00e9rl\u0151t!", "message.confirm.delete.brocadevcs": "Er\u0151s\u00edtsd meg, hogy t\u00f6r\u00f6lni szeretn\u00e9d a Brocade Vcs Switch-et", "message.confirm.delete.ciscoasa1000v": "Er\u0151s\u00edtsd meg, hogy t\u00f6r\u00f6lni akarod a CiscoASA1000v-t", @@ -1354,7 +1352,7 @@ "message.confirm.remove.selected.events": "Er\u0151s\u00edtsd meg, hogy t\u00f6r\u00f6lni szeretn\u00e9d a kiv\u00e1lasztott esem\u00e9nyeket", "message.confirm.remove.vmware.datacenter": "Er\u0151s\u00edtsd meg, hogy el akarod t\u00e1vol\u00edtani a VMware adatk\u00f6zpontot!", "message.confirm.remove.vpc.offering": "Biztos vagy abban, hogy t\u00f6r\u00f6lni akarod ezt a VPC aj\u00e1nlatot?", -"message.confirm.replace.acl.new.one": "Le akarod cser\u00e9lni ez ACL list\u00e1t egy \u00fajjal?", +"message.confirm.replace.acl.new.one": "Le akarod cser\u00e9lni ez ACL List\u00e1t egy \u00fajjal?", "message.confirm.scale.up.router.vm": "Biztosan fel akarod m\u00e9retezni a router VM-et?", "message.confirm.scale.up.system.vm": "Biztosan fel akarod m\u00e9retezni a rendszer VM-et?", "message.confirm.start.lb.vm": "Er\u0151s\u00edtsd meg, hogy el akarod ind\u00edtani az LB VM-et!", @@ -1405,7 +1403,7 @@ "message.guest.traffic.in.basic.zone": "A vend\u00e9g h\u00e1l\u00f3zat forgalma kommunik\u00f3ci\u00f3 v\u00e9gfelhaszn\u00e1l\u00f3i virtu\u00e1lis g\u00e9pek k\u00f6z\u00f6tt. Hat\u00e1rozz meg egy IP c\u00edmtartom\u00e1nyt, amelyb\u0151l a CloudStack a virtu\u00e1lis g\u00e9pekhez rendelhet c\u00edmet. Gy\u0151z\u0151dj meg r\u00f3la, hogy ez a tartom\u00e1ny nincs \u00e1tfed\u00e9sben az elk\u00fcl\u00f6n\u00edtett rendszer IP tartom\u00e1nnyal!", "message.host.dedicated": "Dedik\u00e1lt kiszolg\u00e1l\u00f3", "message.host.dedication.released": "Kiszolg\u00e1l\u00f3 elengedve", -"message.installwizard.copy.whatiscloudstack": "A CloudStack™ egy szoftver, amely sz\u00e1m\u00edt\u00e1si er\u0151forr\u00e1sokat fel\u00fcgyel \u00e9s alkalmas publikus, priv\u00e1t, vagy hibrid infrastrukt\u00fara szolg\u00e1ltat\u00e1s (IaaS) felh\u0151k \u00e9p\u00edt\u00e9s\u00e9re. A CloudStack™ ir\u00e1ny\u00edtja a h\u00e1l\u00f3zatokat, az adatt\u00e1rol\u00f3kat \u00e9s kiszolg\u00e1l\u00f3kat, amelyek a felh\u0151 infrastrukt\u00far\u00e1t alkotj\u00e1k.

A k\u00fcl\u00f6n\u00e1ll\u00f3 virtu\u00e1lis g\u00e9peken t\u00fal a CloudStack™ teljes felh\u0151 insfrastrukt\u00far\u00e1t szolg\u00e1ltat. Ny\u00edlt forr\u00e1sk\u00f3d\u00fa \u00e9s pr\u00e9mium verzi\u00f3k egyar\u00e1nt el\u00e9rhet\u0151ek, a ny\u00edlt forr\u00e1sk\u00f3d\u00fa verzi\u00f3k k\u00f6zel azonos k\u00e9pess\u00e9gekkel rendelkeznek.", +"message.installwizard.copy.whatiscloudstack": "A CloudStack™ egy szoftver, amely sz\u00e1m\u00edt\u00e1si er\u0151forr\u00e1sokat fel\u00fcgyel \u00e9s alkalmas publikus, priv\u00e1t, vagy hibrid infrastrukt\u00fara szolg\u00e1ltat\u00e1s (IaaS) felh\u0151k \u00e9p\u00edt\u00e9s\u00e9re. A CloudStack™ ir\u00e1ny\u00edtja a h\u00e1l\u00f3zatokat, az adatt\u00e1rol\u00f3kat \u00e9s kiszolg\u00e1l\u00f3kat, amelyek a felh\u0151 infrastrukt\u00far\u00e1t alkotj\u00e1k.

A k\u00fcl\u00f6n\u00e1ll\u00f3 virtu\u00e1lis g\u00e9peken t\u00fal a CloudStack™ teljes felh\u0151 insfrastrukt\u00far\u00e1t szolg\u00e1ltat.", "message.installwizard.tooltip.addpod.name": "A pod neve", "message.installwizard.tooltip.addpod.reservedsystemendip": "This is the IP range in the private network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "\u00c1tj\u00e1r\u00f3 a pod kiszolg\u00e1l\u00f3inak.", diff --git a/ui/public/locales/it_IT.json b/ui/public/locales/it_IT.json index fa09e189d98..a2907b71f23 100644 --- a/ui/public/locales/it_IT.json +++ b/ui/public/locales/it_IT.json @@ -14,12 +14,12 @@ "label.account.specific": "Specifico dell'Account", "label.accounts": "Utenti", "label.accounttype": "Account Type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Name", +"label.acl.rule.name": "ACL Name", "label.acquire.new.ip": "Acquisizione nuovo indirizzo IP", "label.acquire.new.secondary.ip": "Acquisizione nuovo IP secondario", "label.action": "Action", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Sessioni Attive", "label.add": "Add", "label.add.account": "Aggiungi un Account", -"label.add.acl": "Aggiungere ACL", -"label.add.acl.list": "Add ACL List", +"label.add.acl.rule": "Aggiungere ACL", +"label.add.acl": "Add ACL", "label.add.affinity.group": "Aggiungere un nuovo gruppo di affinit\u00e0", "label.add.baremetal.dhcp.device": "Add Baremetal DHCP Device", "label.add.bigswitchbcf.device": "Aggiungere Controller BigSwitch BCF", @@ -142,12 +142,12 @@ "label.add.ip.range": "Aggiungere un IP Range", "label.add.isolated.network": "Add Isolated Network", "label.add.ldap.account": "Aggiungi un account LDAP", -"label.add.list.name": "ACL List Name", +"label.add.acl.name": "ACL Name", "label.add.more": "Add More", "label.add.netscaler.device": "Aggiungere device Netscaler", "label.add.network": "Aggiungere una Rete", "label.add.network.acl": "Aggiungere le ACL di rete", -"label.add.network.acl.list": "Add Network ACL List", +"label.add.network.acl": "Add Network ACL", "label.add.network.offering": "Aggiungere offerta di rete", "label.add.new.gateway": "Aggiungere un nuovo gateway", "label.add.new.tier": "Aggiungere un nuovo livello", @@ -334,7 +334,7 @@ "label.default.use": "Default Use", "label.default.view": "Vista di default", "label.delete": "Cancellare", -"label.delete.acl.list": "Delete ACL List", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "Cancellare Gruppo di Affinit\u00e0", "label.delete.alerts": "Cancella allarmi", "label.delete.bigswitchbcf": "Rimuovere Controller BigSwitch BCF", @@ -419,7 +419,7 @@ "label.dpd": "Dead Peer Detection", "label.driver": "Driver", "label.edit": "Modifica", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.project.details": "Modificare i dettagli del progetto", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "Remove VPC offering", "label.removing": "Rimozione", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL List", "label.required": "Required", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires Upgrade", @@ -1150,8 +1149,7 @@ "label.usehttps": "Utilizzare HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "User", -"label.userdata": "Userdata", -"label.userdatal2": "User Data", +"label.user.data": "User Data", "label.username": "Username", "label.users": "Users", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts", "message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events", "message.confirm.attach.disk": "Are you sure you want to attach disk?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v", @@ -1405,7 +1403,7 @@ "message.guest.traffic.in.basic.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications.", "message.installwizard.tooltip.addpod.name": "Un nome per il pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Questo \u00e8 l'intervallo di indirizzi IP nella rete privata che CloudStack utilizza per la gestione delle VM del Secondary Storage e le VM della Console Proxy. Questi indirizzi IP sono ricavati dalla stessa subnet dei server computazionali.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "Il gateway per gli host appartenenti al pod.", diff --git a/ui/public/locales/ja_JP.json b/ui/public/locales/ja_JP.json index 53009781500..3ab374a2a06 100644 --- a/ui/public/locales/ja_JP.json +++ b/ui/public/locales/ja_JP.json @@ -58,11 +58,11 @@ "label.access.kubernetes.nodes": "Kubernetesノードに接続", "label.acl.export": "エクスポートACLs", "label.acl.id": "ACL ID", - "label.acl.list.rules": "ACLルールのリスト", + "label.acl.rules": "ACLルールのリスト", "label.acl.reason.description": "ACLルールの理由を入力してください。", "label.acl.replaced": "ACLが置き換えられました", "label.aclid": "ACL", - "label.aclname": "ACL名", + "label.acl.rule.name": "ACL名", "label.acltotal": "ネットワークACL合計", "label.acquire.new.ip": "新しいIPアドレスの取得", "label.acquire.new.secondary.ip": "セカンダリIPアドレスの取得", @@ -298,8 +298,8 @@ "label.add.account": "アカウント追加", "label.add.accounts": "アカウント追加", "label.add.accounts.to": "アカウントの追加先:", - "label.add.acl": "ACL追加", - "label.add.acl.list": "ACL一覧追加", + "label.add.acl.rule": "ACL追加", + "label.add.acl": "ACL一覧追加", "label.add.affinity.group": "新しいアフィニティグループ追加", "label.add.baremetal.dhcp.device": "ベアメタルDHCPデバイス追加", "label.add.baremetal.rack.configuration": "ベアメタルラック設定追加", @@ -333,14 +333,14 @@ "label.add.l2.guest.network": "L2ゲストネットワーク追加", "label.add.ldap.account": "LDAPアカウント追加", "label.add.ldap.list.users": "LDAPユーザー一覧", - "label.add.list.name": "ACL一覧名", + "label.add.acl.name": "ACL一覧名", "label.add.load.balancer": "ロードバランサー追加", "label.add.management.ip.range": "マネージメントIP範囲追加", "label.add.more": "その他の項目追加", "label.add.netscaler.device": "NetScalerデバイス追加", "label.add.network": "ネットワーク追加", "label.add.network.acl": "ネットワークACL追加", - "label.add.network.acl.list": "ネットワークACL一覧追加", + "label.add.network.acl": "ネットワークACL一覧追加", "label.add.network.device": "ネットワークデバイス追加", "label.add.network.offering": "ネットワークオファリング追加", "label.add.new.f5": "新しいF5追加", @@ -718,7 +718,7 @@ "label.default.view": "デフォルトビュー", "label.defaultnetwork": "デフォルトネットワーク", "label.delete": "削除", - "label.delete.acl.list": "ACL一覧削除", + "label.delete.acl": "ACL一覧削除", "label.delete.affinity.group": "アフィニティグループ削除", "label.delete.alerts": "アラート削除", "label.delete.backup": "バックアップ削除", @@ -872,7 +872,7 @@ "label.dynamicscalingenabled": "ダイナミックスケーリング有効", "label.dynamicscalingenabled.tooltip": "テンプレート、サービスオファリング、およびグローバル設定で動的スケーリングが有効になっている場合にのみ、VMは動的にスケーリングできます。", "label.edit": "編集", - "label.edit.acl.list": "ACL一覧編集", + "label.edit.acl": "ACL一覧編集", "label.edit.acl.rule": "ACLルール編集", "label.edit.affinity.group": "アフィニティグループ編集", "label.edit.lb.rule": "LBルール編集", @@ -1498,7 +1498,7 @@ "label.netscaler.vpx": "NetScaler VPXロードバランサー", "label.network": "ネットワーク", "label.network.acl": "ネットワークACL", - "label.network.acl.lists": "ネットワークACL一覧", + "label.network.acls": "ネットワークACL一覧", "label.network.acls": "ネットワークACL", "label.network.addvm": "VMへのネットワーク追加", "label.network.desc": "ネットワークの説明", @@ -1871,7 +1871,6 @@ "label.removing": "削除しています", "label.removing.user": "ユーザーを削除しています", "label.replace.acl": "ACLの置き換え", - "label.replace.acl.list": "ACL一覧の置き換え", "label.report.bug": "問題レポート", "label.required": "必須です", "label.requireshvm": "HVM", @@ -2321,8 +2320,7 @@ "label.user.details": "ユーザーの詳細", "label.user.source": "ソース", "label.user.vm": "ユーザーVM", - "label.userdata": "ユーザーデータ", - "label.userdatal2": "ユーザーデータ", + "label.user.data": "ユーザーデータ", "label.username": "ユーザー名", "label.users": "ユーザー", "label.usersource": "ユーザータイプ", @@ -2727,7 +2725,7 @@ "message.confirm.dedicate.host.domain.account": "このホストをドメイン/アカウント専用に設定してもよろしいですか?", "message.confirm.dedicate.pod.domain.account": "このポッドをドメイン/アカウント専用に設定してもよろしいですか?", "message.confirm.dedicate.zone": "このゾーンをドメイン/アカウント専用に設定してもよろしいですか?", - "message.confirm.delete.acl.list": "このACL一覧を削除してもよろしいですか?", + "message.confirm.delete.acl": "このACL一覧を削除してもよろしいですか?", "message.confirm.delete.alert": "このアラートを削除してもよろしいですか?", "message.confirm.delete.baremetal.rack.configuration": "ベアメタルラック設定を削除してもよろしいですか?", "message.confirm.delete.bigswitchbcf": "このBigSwitchBCFコントローラーを削除してもよろしいですか?", @@ -3063,7 +3061,7 @@ "message.installwizard.copy.whatisahost": "ホストは単一のコンピューターで、ゲスト仮想マシンを実行するコンピューティング リソースをオファリングします。ベア メタル ホストを除いて、各ホストにはゲスト仮想マシンを管理するためのハイパーバイザー ソフトウェアをインストールします。ベア メタル ホストについては、『インストールガイド上級編』で特殊例として説明します。たとえば、KVM が有効な Linux サーバー、Citrix XenServer が動作するサーバー、および ESXi サーバーがホストです。基本インストールでは、XenServer または KVM を実行する単一のホストを使用します。

ホストは CloudStack™ 環境内の最小の組織単位です。ホストはクラスターに含まれ、クラスターはポッドに含まれ、ポッドはゾーンに含まれます。", "message.installwizard.copy.whatisapod": "通常、1つのポッドは単一のラックを表します。同じポッド内のホストは同じサブネットに含まれます。

ポッドはCloudStack™環境内の2番目に大きな組織単位です。ポッドはゾーンに含まれます。各ゾーンは1つ以上のポッドを含むことができます。基本インストールでは、ゾーン内のポッドは1つです。", "message.installwizard.copy.whatisazone": "ゾーンはCloudStack™環境内の最大の組織単位です。1つのデータセンター内に複数のゾーンを設定できますが、通常、ゾーンは単一のデータセンターに相当します。インフラストラクチャをゾーンに組織化すると、ゾーンを物理的に分離して冗長化することができます。たとえば、各ゾーンに電源とネットワークアップリンクを配備します。必須ではありませんが、ゾーンは遠隔地に分散することができます。", - "message.installwizard.copy.whatiscloudstack": "CloudStack™はコンピューティングリソースをプールするソフトウェアプラットフォームで、パブリック、プライベート、およびハイブリッドのInfrastructure as a Service(IaaS)クラウドを構築することができます。CloudStack™を使用して、クラウドインフラストラクチャを構成するネットワーク、ストレージ、およびコンピューティングノードを管理し、クラウドコンピューティング環境を展開、管理、および構成します。

CloudStack™はコモディティ化したハードウェア上で動作する個別の仮想マシンイメージを超えて拡張することができ、簡単な設定で動作するクラウドインフラストラクチャのソフトウェアスタックによって、仮想データセンターつまり多層型のマルチテナントクラウドアプリケーションをサービスとして構築し、展開し、管理するために不可欠なコンポーネントがすべてオファリングされます。オープンソースバージョンとプレミアムバージョンの両方がオファリングされますが、オープンソースバージョンでもほとんどの機能を使用できます。", + "message.installwizard.copy.whatiscloudstack": "CloudStack™はコンピューティングリソースをプールするソフトウェアプラットフォームで、パブリック、プライベート、およびハイブリッドのInfrastructure as a Service(IaaS)クラウドを構築することができます。CloudStack™を使用して、クラウドインフラストラクチャを構成するネットワーク、ストレージ、およびコンピューティングノードを管理し、クラウドコンピューティング環境を展開、管理、および構成します。

CloudStack™はコモディティ化したハードウェア上で動作する個別の仮想マシンイメージを超えて拡張することができ、簡単な設定で動作するクラウドインフラストラクチャのソフトウェアスタックによって、仮想データセンターつまり多層型のマルチテナントクラウドアプリケーションをサービスとして構築し、展開し、管理するために不可欠なコンポーネントがすべてオファリングされます。", "message.installwizard.copy.whatisprimarystorage": "CloudStack™のクラウドインフラストラクチャでは、プライマリストレージとセカンダリストレージの2種類のストレージを使用します。どちらのストレージにも、iSCSI、NFSサーバー、またはローカルディスクを使用できます。

プライマリストレージはクラスターに関連付けられ、そのクラスター内のホストで動作するすべてのVMの各ゲストVMのディスクボリュームを格納します。通常、プライマリストレージサーバーはホストの近くに設置します。", "message.installwizard.copy.whatissecondarystorage": "セカンダリストレージはゾーンと関連付けられ、次の項目を格納します。
  • テンプレート - VMの起動に使用できるOSイメージで、アプリケーションのインストールなど追加の構成を含めることができます。
  • ISOイメージ - 起動可能または起動不可のOSイメージです。
  • ディスクボリュームのスナップショット - VMデータの保存コピーです。データの復元または新しいテンプレートの作成に使用できます。
", "message.installwizard.now.building": "クラウドを構築しています...", diff --git a/ui/public/locales/ko_KR.json b/ui/public/locales/ko_KR.json index 153bfd64cef..cc9683633be 100644 --- a/ui/public/locales/ko_KR.json +++ b/ui/public/locales/ko_KR.json @@ -32,10 +32,10 @@ "label.accounttype": "\uacc4\uc815 \uc720\ud615", "label.acl.export": "ACL \ub0b4\ubcf4\ub0b4\uae30", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL \ubaa9\ub85d \uaddc\uce59", +"label.acl.rules": "ACL \ubaa9\ub85d \uaddc\uce59", "label.acl.reason.description": "ACL \uaddc\uce59 \ub4a4\uc5d0 \uc124\uba85\uc744 \uc785\ub825\ud558\uc2ed\uc2dc\uc624.", "label.aclid": "ACL", -"label.aclname": "ACL \uc774\ub984", +"label.acl.rule.name": "ACL \uc774\ub984", "label.acquire.new.ip": "\uc0c8 IP \uc8fc\uc18c \uac00\uc838\uc624\uae30", "label.acquire.new.secondary.ip": "\uc0c8 \ubcf4\uc870 IP \uc8fc\uc18c \uac00\uc838\uc624\uae30", "label.acquiring.ip": "IP \uac00\uc838\uc624\uae30", @@ -153,8 +153,8 @@ "label.activeviewersessions": "\ud65c\uc131 \uc138\uc158", "label.add": "\ucd94\uac00", "label.add.account": "\uacc4\uc815 \ucd94\uac00", -"label.add.acl": "\uad8c\ud55c \uad00\ub9ac(ACL) \ucd94\uac00", -"label.add.acl.list": "ACL \ubaa9\ub85d \ucd94\uac00", +"label.add.acl.rule": "\uad8c\ud55c \uad00\ub9ac(ACL) \ucd94\uac00", +"label.add.acl": "ACL \ubaa9\ub85d \ucd94\uac00", "label.add.affinity.group": "\uc0c8 Affinity \uadf8\ub8f9 \ucd94\uac00", "label.add.baremetal.dhcp.device": "Baremetal DHCP \uc7a5\uce58 \ucd94\uac00", "label.add.bigswitchbcf.device": "BigSwitch BCF \ucee8\ud2b8\ub864\ub7ec \ucd94\uac00", @@ -178,12 +178,12 @@ "label.add.isolated.network": "isolated \ub124\ud2b8\uc6cc\ud06c \ucd94\uac00", "label.add.kubernetes.cluster": "\ucfe0\ubc84\ub124\ud14c\uc2a4 \ud074\ub7ec\uc2a4\ud130 \ucd94\uac00", "label.add.ldap.account": "LDAP \uacc4\uc815 \ucd94\uac00", -"label.add.list.name": "ACL \ubaa9\ub85d \uc774\ub984", +"label.add.acl.name": "ACL \ubaa9\ub85d \uc774\ub984", "label.add.more": "\ub2e4\ub978 \ud56d\ubaa9 \ucd94\uac00", "label.add.netscaler.device": "Netscaler \uc7a5\uce58 \ucd94\uac00", "label.add.network": "\ub124\ud2b8\uc6cc\ud06c \ucd94\uac00", "label.add.network.acl": "\ub124\ud2b8\uc6cc\ud06c \uad8c\ud55c \uad00\ub9ac(ACL) \ucd94\uac00", -"label.add.network.acl.list": "\ub124\ud2b8\uc6cc\ud06c ACL \ubaa9\ub85d \ucd94\uac00", +"label.add.network.acl": "\ub124\ud2b8\uc6cc\ud06c ACL \ubaa9\ub85d \ucd94\uac00", "label.add.network.offering": "\ub124\ud2b8\uc6cc\ud06c \uc624\ud37c\ub9c1 \ucd94\uac00", "label.add.new.gateway": "\uc0c8 \uac8c\uc774\ud2b8\uc6e8\uc774 \ucd94\uac00\ud558\uae30", "label.add.new.tier": "\uc0c8 \uc11c\ube0c\ub137 \ucd94\uac00", @@ -436,7 +436,7 @@ "label.default.view": "\uae30\ubcf8 \ubcf4\uae30", "label.defaultnetwork": "\uae30\ubcf8 \ub124\ud2b8\uc6cc\ud06c", "label.delete": "\uc0ad\uc81c", -"label.delete.acl.list": "ACL \ubaa9\ub85d \uc0ad\uc81c", +"label.delete.acl": "ACL \ubaa9\ub85d \uc0ad\uc81c", "label.delete.affinity.group": "Affinity \uadf8\ub8f9 \uc0ad\uc81c", "label.delete.alerts": "\uc54c\ub9bc \uc0ad\uc81c", "label.delete.backup": "\ubc31\uc5c5 \uc0ad\uc81c", @@ -549,7 +549,7 @@ "label.dpd": "Dead \ud53c\uc5b4 \uac10\uc9c0", "label.driver": "\ub4dc\ub77c\uc774\ubc84", "label.edit": "\ud3b8\uc9d1", -"label.edit.acl.list": "ACL \ubaa9\ub85d \ud3b8\uc9d1", +"label.edit.acl": "ACL \ubaa9\ub85d \ud3b8\uc9d1", "label.edit.acl.rule": "ACL \uaddc\uce59 \ud3b8\uc9d1", "label.edit.project.details": "\ud504\ub85c\uc81d\ud2b8 \uc0c1\uc138 \ud3b8\uc9d1", "label.edit.project.role": "\ud504\ub85c\uc81d\ud2b8 \uc5ed\ud560 \ud3b8\uc9d1", @@ -985,7 +985,7 @@ "label.netscaler.vpx": "NetScaler VPX \ub85c\ub4dc\ubc38\ub7f0\uc11c", "label.network": "\ub124\ud2b8\uc6cc\ud06c", "label.network.acl": "\ub124\ud2b8\uc6cc\ud06c \uad8c\ud55c \uad00\ub9ac(ACL)", -"label.network.acl.lists": "Network ACL \ubaa9\ub85d", +"label.network.acls": "Network ACL \ubaa9\ub85d", "label.network.addvm": "VM\uc5d0 \ub124\ud2b8\uc6cc\ud06c \ucd94\uac00", "label.network.desc": "\ub124\ud2b8\uc6cc\ud06c \uc124\uba85", "label.network.domain": "\ub124\ud2b8\uc6cc\ud06c \ub3c4\uba54\uc778", @@ -1251,7 +1251,6 @@ "label.remove.vpc.offering": "VPC \uc624\ud37c\ub9c1 \uc0ad\uc81c", "label.removing": "\uc0ad\uc81c\ud558\ub294 \uc911...", "label.replace.acl": "ACL \uad50\uccb4", -"label.replace.acl.list": "ACL \ubaa9\ub85d \uad50\uccb4", "label.report.bug": "\uc774\uc288 \ub9ac\ud3ec\ud2b8", "label.required": "\ud544\uc218 \uc0ac\ud56d", "label.requireshvm": "HVM", @@ -1560,8 +1559,7 @@ "label.usenewdiskoffering": "\ub514\uc2a4\ud06c \uc624\ud37c\ub9c1\uc744 \ubcc0\uacbd\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "label.user": "\uc0ac\uc6a9\uc790", "label.user.conflict": "\ucda9\ub3cc", -"label.userdata": "\uc0ac\uc6a9\uc790 \ub370\uc774\ud130", -"label.userdatal2": "\uc0ac\uc6a9\uc790 \ub370\uc774\ud130", +"label.user.data": "\uc0ac\uc6a9\uc790 \ub370\uc774\ud130", "label.username": "\uc0ac\uc6a9\uc790 \uc774\ub984", "label.users": "\uc0ac\uc6a9\uc790", "label.usersource": "\uc0ac\uc6a9\uc790 \uc720\ud615", @@ -1818,7 +1816,7 @@ "message.confirm.archive.selected.events": "\uc120\ud0dd\ud55c \uc774\ubca4\ud2b8\ub97c \ubcf4\uad00\ud560 \uac83\uc778\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", "message.confirm.attach.disk": "\ub514\uc2a4\ud06c\ub97c \uc5f0\uacb0 \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "message.confirm.configure.ovs": "Ovs\ub97c \uad6c\uc131\ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", -"message.confirm.delete.acl.list": "\uc774 ACL \ubaa9\ub85d\uc744 \uc0ad\uc81c \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", +"message.confirm.delete.acl": "\uc774 ACL \ubaa9\ub85d\uc744 \uc0ad\uc81c \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?", "message.confirm.delete.bigswitchbcf": "\uc774 BigSwitch BCF \ucee8\ud2b8\ub864\ub7ec\ub97c \uc0ad\uc81c\ud560 \uac83\uc778\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", "message.confirm.delete.brocadevcs": "Brocade Vcs \uc2a4\uc704\uce58\ub97c \uc0ad\uc81c\ud560 \uac83\uc778\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", "message.confirm.delete.ciscoasa1000v": "CiscoASA1000\uc744 \uc0ad\uc81c\ud560 \uac83\uc778\uc9c0 \ud655\uc778\ud558\uc2ed\uc2dc\uc624.", @@ -2052,7 +2050,7 @@ "message.host.dedicated": "\uc804\uc6a9 \ud638\uc2a4\ud2b8", "message.host.dedication.released": "\uc804\uc6a9 \ud638\uc2a4\ud2b8 \ucd9c\uc2dc", "message.info.cloudian.console": "Cloudian \uad00\ub9ac \ucf58\uc194\uc774 \ub2e4\ub978 \ucc3d\uc5d0\uc11c \uc5f4\ub9bd\ub2c8\ub2e4.", -"message.installwizard.copy.whatiscloudstack": "CloudStack\u2122\uc740 \ucef4\ud4e8\ud305 \ub9ac\uc18c\uc2a4\ub97c \ud3ec\ud568\ud558\uc5ec \uacf5\uac1c, \uc0ac\uc124, \ubc0f \ud558\uc774\ube0c\ub9ac\ub4dc\uc758 Infrastructure as a Service (IaaS) \ud074\ub77c\uc6b0\ub4dc\ub97c \uad6c\ucd95\ud560 \uc218 \uc788\ub294 \uc18c\ud504\ud2b8\uc6e8\uc5b4 \ud50c\ub7ab\ud3fc\uc785\ub2c8\ub2e4. CloudStack \u2122\uc740 \ud074\ub77c\uc6b0\ub4dc \uc778\ud504\ub77c\ub97c \uad6c\uc131\ud558\ub294 \ub124\ud2b8\uc6cc\ud06c, \uc2a4\ud1a0\ub9ac\uc9c0 \ubc0f \ucef4\ud4e8\ud305 \ub178\ub4dc\ub97c \uad00\ub9ac\ud569\ub2c8\ub2e4. CloudStack \u2122\uc744 \uc0ac\uc6a9\ud558\uc5ec \ud074\ub77c\uc6b0\ub4dc \ucef4\ud4e8\ud305 \ud658\uacbd\uc744 \ubc30\ud3ec, \uad00\ub9ac \ubc0f \uad6c\uc131\ud558\uc2ed\uc2dc\uc624. \n\nCloudStack \u2122 \uc77c\ubc18 \ud558\ub4dc\uc6e8\uc5b4\uc5d0\uc11c \uc2e4\ud589\ub418\ub294 \uac1c\ubcc4 \uac00\uc0c1\uba38\uc2e0 \uc774\ubbf8\uc9c0\ub97c \ub118\uc5b4 \ud655\uc7a5\ub418\uc5b4 \uac00\uc0c1 \ub370\uc774\ud130 \uc13c\ud130\ub97c \uc11c\ube44\uc2a4\ub85c \uc81c\uacf5\ud558\uae30\uc704\ud55c \ud134\ud0a4 \ud074\ub77c\uc6b0\ub4dc \uc778\ud504\ub77c \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc2a4\ud0dd\uc744 \uc81c\uacf5\ud558\uc5ec \ubaa8\ub4e0 \ud544\uc218 \uae30\ub2a5\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4. \ub2e4\uc911 \uacc4\uce35 \ubc0f \ub2e4\uc911 \ud14c\ub10c\ud2b8 \ud074\ub77c\uc6b0\ub4dc \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ube4c\ub4dc, \ubc30\ud3ec \ubc0f \uad00\ub9ac\ud558\uae30\uc704\ud55c \uad6c\uc131 \uc694\uc18c\uc785\ub2c8\ub2e4. \uc624\ud508 \uc18c\uc2a4 \ubc0f \ud504\ub9ac\ubbf8\uc5c4 \ubc84\uc804\uc744 \ubaa8\ub450 \uc0ac\uc6a9\ud560 \uc218 \uc788\uc73c\uba70 \uc624\ud508 \uc18c\uc2a4 \ubc84\uc804\uc740 \uac70\uc758 \ub3d9\uc77c\ud55c \uae30\ub2a5\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4.", +"message.installwizard.copy.whatiscloudstack": "CloudStack\u2122\uc740 \ucef4\ud4e8\ud305 \ub9ac\uc18c\uc2a4\ub97c \ud3ec\ud568\ud558\uc5ec \uacf5\uac1c, \uc0ac\uc124, \ubc0f \ud558\uc774\ube0c\ub9ac\ub4dc\uc758 Infrastructure as a Service (IaaS) \ud074\ub77c\uc6b0\ub4dc\ub97c \uad6c\ucd95\ud560 \uc218 \uc788\ub294 \uc18c\ud504\ud2b8\uc6e8\uc5b4 \ud50c\ub7ab\ud3fc\uc785\ub2c8\ub2e4. CloudStack \u2122\uc740 \ud074\ub77c\uc6b0\ub4dc \uc778\ud504\ub77c\ub97c \uad6c\uc131\ud558\ub294 \ub124\ud2b8\uc6cc\ud06c, \uc2a4\ud1a0\ub9ac\uc9c0 \ubc0f \ucef4\ud4e8\ud305 \ub178\ub4dc\ub97c \uad00\ub9ac\ud569\ub2c8\ub2e4. CloudStack \u2122\uc744 \uc0ac\uc6a9\ud558\uc5ec \ud074\ub77c\uc6b0\ub4dc \ucef4\ud4e8\ud305 \ud658\uacbd\uc744 \ubc30\ud3ec, \uad00\ub9ac \ubc0f \uad6c\uc131\ud558\uc2ed\uc2dc\uc624. \n\nCloudStack \u2122 \uc77c\ubc18 \ud558\ub4dc\uc6e8\uc5b4\uc5d0\uc11c \uc2e4\ud589\ub418\ub294 \uac1c\ubcc4 \uac00\uc0c1\uba38\uc2e0 \uc774\ubbf8\uc9c0\ub97c \ub118\uc5b4 \ud655\uc7a5\ub418\uc5b4 \uac00\uc0c1 \ub370\uc774\ud130 \uc13c\ud130\ub97c \uc11c\ube44\uc2a4\ub85c \uc81c\uacf5\ud558\uae30\uc704\ud55c \ud134\ud0a4 \ud074\ub77c\uc6b0\ub4dc \uc778\ud504\ub77c \uc18c\ud504\ud2b8\uc6e8\uc5b4 \uc2a4\ud0dd\uc744 \uc81c\uacf5\ud558\uc5ec \ubaa8\ub4e0 \ud544\uc218 \uae30\ub2a5\uc744 \uc81c\uacf5\ud569\ub2c8\ub2e4. \ub2e4\uc911 \uacc4\uce35 \ubc0f \ub2e4\uc911 \ud14c\ub10c\ud2b8 \ud074\ub77c\uc6b0\ub4dc \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \ube4c\ub4dc, \ubc30\ud3ec \ubc0f \uad00\ub9ac\ud558\uae30\uc704\ud55c \uad6c\uc131 \uc694\uc18c\uc785\ub2c8\ub2e4.", "message.installwizard.tooltip.addpod.name": "Pod \uc774\ub984\uc785\ub2c8\ub2e4.", "message.installwizard.tooltip.addpod.reservedsystemendip": "\uc774\uac83\uc740 2\ucc28 \uc2a4\ud1a0\ub9ac\uc9c0 VM \ubc0f \ucf58\uc194 \ud504\ub85d\uc2dc VM\ub97c \uad00\ub9ac\ud558\uae30 \uc704\ud574\uc11c CloudStack\uc5d0\uc11c \uc0ac\uc6a9\ud558\ub294 \uc0ac\uc124 \ub124\ud2b8\uc6cc\ud06c\ub0b4 IP \uc8fc\uc18c \ubc94\uc704\uc785\ub2c8\ub2e4. \uc774\ub7ec\ud55c IP \uc8fc\uc18c\ub294 \ucef4\ud4e8\ud305 \uc11c\ubc84\uc640 \uac19\uc740 \uc11c\ube0c\ub124\ud2b8\uc6cc\ud06c\uc5d0\uc11c \ud560\ub2f9\ud569\ub2c8\ub2e4.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "\ud604\uc7ac Pod\ub0b4 \ud638\uc2a4\ud2b8 \uac8c\uc774\ud2b8\uc6e8\uc774\uc785\ub2c8\ub2e4.", diff --git a/ui/public/locales/nb_NO.json b/ui/public/locales/nb_NO.json index a0fbd8927df..586d1fc5b25 100644 --- a/ui/public/locales/nb_NO.json +++ b/ui/public/locales/nb_NO.json @@ -14,12 +14,12 @@ "label.account.specific": "Kontospesifikk", "label.accounts": "Kontoer", "label.accounttype": "Kontotype", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL Liste Regler", +"label.acl.rules": "ACL Liste Regler", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Navn", +"label.acl.rule.name": "ACL Navn", "label.acquire.new.ip": "Tilegne ny IP", "label.acquire.new.secondary.ip": "Tilegne ny sekund\u00e6r IP", "label.action": "Handling", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Aktive sesjoner", "label.add": "Legg til", "label.add.account": "Legg til konto", -"label.add.acl": "Legg til ACL", -"label.add.acl.list": "Legg til ACL liste", +"label.add.acl.rule": "Legg til ACL", +"label.add.acl": "Legg til ACL liste", "label.add.affinity.group": "Legg til affinitetsgruppe", "label.add.baremetal.dhcp.device": "Legg Til Barmetall DHCP Enhet", "label.add.bigswitchbcf.device": "Legg til BigSwitch BCF kontroller", @@ -142,12 +142,12 @@ "label.add.ip.range": "Legg til IP-rekke", "label.add.isolated.network": "Legg Til Isolert Nettverk", "label.add.ldap.account": "Legg til LDAP-konto", -"label.add.list.name": "ACL listenavn", +"label.add.acl.name": "ACL listenavn", "label.add.more": "Legg til mer", "label.add.netscaler.device": "Legg til Netscaler enhet", "label.add.network": "Legg til nettverk", "label.add.network.acl": "Legg til nettverk ACL", -"label.add.network.acl.list": "Legg til nettverk ACL liste", +"label.add.network.acl": "Legg til nettverk ACL liste", "label.add.network.offering": "Legg til nettverkstilbud", "label.add.new.gateway": "Legg til ny gateway", "label.add.new.tier": "Legg til ny gren", @@ -334,7 +334,7 @@ "label.default.use": "Standard bruk", "label.default.view": "Standardvisning", "label.delete": "Slett", -"label.delete.acl.list": "Slett ACL liste", +"label.delete.acl": "Slett ACL liste", "label.delete.affinity.group": "Slett affinitetsgruppe", "label.delete.alerts": "Slette varsler", "label.delete.bigswitchbcf": "Fjern BigSwitch BCF-kontroller", @@ -419,7 +419,7 @@ "label.dpd": "D\u00f8d endepunkt-deteksjon", "label.driver": "Driver", "label.edit": "Editer", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Endre ACL regel", "label.edit.project.details": "Editer prosjektdetaljer", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "Fjern VPC tilbud", "label.removing": "Fjerner", "label.replace.acl": "Erstatt ACL", -"label.replace.acl.list": "Erstatt ACL Liste", "label.required": "P\u00e5krevd", "label.requireshvm": "HVM", "label.requiresupgrade": "Krever oppgradering", @@ -1150,8 +1149,7 @@ "label.usehttps": "Bruk HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Bruker", -"label.userdata": "Brukerdata", -"label.userdatal2": "Brukerdata", +"label.user.data": "Brukerdata", "label.username": "Brukernavn", "label.users": "Brukere", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Vennligst bekreft at du \u00f8nsker \u00e5 arkivere valgte varsler", "message.confirm.archive.selected.events": "Vennligst bekreft at du vil arkivere valgte hendelser", "message.confirm.attach.disk": "Er du sikker p\u00e5 at du vil tildele disk?", -"message.confirm.delete.acl.list": "Er du sikker p\u00e5 at du \u00f8nsker \u00e5 slette denne ACL listen?", +"message.confirm.delete.acl": "Er du sikker p\u00e5 at du \u00f8nsker \u00e5 slette denne ACL listen?", "message.confirm.delete.bigswitchbcf": "Vennligst bekreft at du \u00f8nsker \u00e5 slette denne BigSwitch BCF Controlleren?", "message.confirm.delete.brocadevcs": "Vennligst bekreft at du vil slette denne Brocade Vcs svitsjen", "message.confirm.delete.ciscoasa1000v": "Vennligst bekreft at du vil slette CiscoASA1000v", @@ -1405,7 +1403,7 @@ "message.guest.traffic.in.basic.zone": "Gjestetrafikk er kommunikasjon mellom sluttbrukers virtuelle gjester. Spesifiser en rekke med IP-adresser som CloudStack kan tildele virtuelle gjester. S\u00f8rg for at denne rekken ikke overlapper IP-rekken(e) som er reservert systemmaskiner.", "message.host.dedicated": "Dedikert Vert", "message.host.dedication.released": "Vert dedikering frigitt", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ er en programvare-plattform som grupperer dataressurser for \u00e5 bygge offentlige, private og hybride infrastruktur som en tjeneste (IaaS) skyer. CloudStack™ administrerer nettverk, lagring og regnekraft-verter som til sammen blir en sky-infrastruktur. Bruk CloudStack™ til \u00e5 distribuere, administrerre og konfigurere dine skyressurser og milj\u00f8er.

Utvid forbi individuelle virtuelle gjester som kj\u00f8rer p\u00e5 typisk maskinvare, CloudStack™ gir en skybasert infrastruktur-programvare for levering av virtuelle datasentre som en tjeneste - lever alle de essensielle komponenente for \u00e5 bygge, distribuere og administrere multi-tier og multi-tenant sky-applikasjoner. B\u00e5de fri kildekode- og premium-versjoner er tilgjengelig, hvor den fri kildekode-basert gir tiln\u00e6rmet samme funksjonalitet.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ er en programvare-plattform som grupperer dataressurser for \u00e5 bygge offentlige, private og hybride infrastruktur som en tjeneste (IaaS) skyer. CloudStack™ administrerer nettverk, lagring og regnekraft-verter som til sammen blir en sky-infrastruktur. Bruk CloudStack™ til \u00e5 distribuere, administrerre og konfigurere dine skyressurser og milj\u00f8er.

Utvid forbi individuelle virtuelle gjester som kj\u00f8rer p\u00e5 typisk maskinvare, CloudStack™ gir en skybasert infrastruktur-programvare for levering av virtuelle datasentre som en tjeneste - lever alle de essensielle komponenente for \u00e5 bygge, distribuere og administrere multi-tier og multi-tenant sky-applikasjoner.", "message.installwizard.tooltip.addpod.name": "Et navn for poden", "message.installwizard.tooltip.addpod.reservedsystemendip": "Dette er IP-rekken i det private nettverket som CloudStack bruker for \u00e5 administrere Sekund\u00e6rlagrins-servere og Konsollproxy-servere. Disse IP-adressene tas fra samme subnett som regnekraft-serverne.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "Gatewayen til vertene i poden.", diff --git a/ui/public/locales/nl_NL.json b/ui/public/locales/nl_NL.json index 8e637f190b3..15b1bbb1394 100644 --- a/ui/public/locales/nl_NL.json +++ b/ui/public/locales/nl_NL.json @@ -14,12 +14,12 @@ "label.account.specific": "Account-specifiek", "label.accounts": "Accounts", "label.accounttype": "Account type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL lijst regels", +"label.acl.rules": "ACL lijst regels", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL naam", +"label.acl.rule.name": "ACL naam", "label.acquire.new.ip": "Bemachtig nieuw IP", "label.acquire.new.secondary.ip": "Verkrijg nieuw secundair IP", "label.action": "Actie", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Actieve Sessies", "label.add": "Voeg toe", "label.add.account": "Voeg Account toe", -"label.add.acl": "Voeg ACL toe", -"label.add.acl.list": "voeg een ACL lijst toe", +"label.add.acl.rule": "Voeg ACL toe", +"label.add.acl": "voeg een ACL lijst toe", "label.add.affinity.group": "Nieuwe affinity groep toevoegen", "label.add.baremetal.dhcp.device": "Voeg Baremetal DHCP Apparaat toe", "label.add.bigswitchbcf.device": "Voeg eenBigSwitch BCF controller toe", @@ -142,12 +142,12 @@ "label.add.ip.range": "Voeg IP range toe", "label.add.isolated.network": "Geisoleerd Netwerk Toevoegen", "label.add.ldap.account": "Voeg LDAP account toe", -"label.add.list.name": "ACL lijst naam", +"label.add.acl.name": "ACL lijst naam", "label.add.more": "Voeg meer toe", "label.add.netscaler.device": "Voeg Netscaler apparaat toe", "label.add.network": "Voeg Netwerk toe", "label.add.network.acl": "Voeg netwerk ACL toe", -"label.add.network.acl.list": "voeg netwerk ACL lijst toe", +"label.add.network.acl": "voeg netwerk ACL lijst toe", "label.add.network.offering": "Voeg netwerk aanbieding toe", "label.add.new.gateway": "Voeg nieuwe gateway toe", "label.add.new.tier": "Voeg nieuwe Tier toe", @@ -334,7 +334,7 @@ "label.default.use": "Standaard Gebruik", "label.default.view": "Standaard Weergave", "label.delete": "Verwijder", -"label.delete.acl.list": "verwijder ACL lijst", +"label.delete.acl": "verwijder ACL lijst", "label.delete.affinity.group": "Verwijder Affinity Groep", "label.delete.alerts": "Verwijder waarschuwingen", "label.delete.bigswitchbcf": "Verwijder BigSwitch BCF Controller", @@ -420,7 +420,7 @@ "label.driver": "Driver", "label.dynamicscalingenabled": "Dynamisch schalen ingeschakeld\n", "label.edit": "Wijzig", -"label.edit.acl.list": "Verander een ACL lijst", +"label.edit.acl": "Verander een ACL lijst", "label.edit.acl.rule": "wijzig ACL regel", "label.edit.project.details": "Wijzig project details", "label.edit.role": "Edit Role", @@ -925,7 +925,6 @@ "label.remove.vpc.offering": "VPC aanbieding verwijderen", "label.removing": "Verwijderen", "label.replace.acl": "vervang ACL", -"label.replace.acl.list": "vervang ACL lijst", "label.required": "Vereist", "label.requireshvm": "HVM", "label.requiresupgrade": "Upgrade Benodigd", @@ -1151,8 +1150,7 @@ "label.usehttps": "Gebruik HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "Gebruiker", -"label.userdata": "Gebruikers gegevens", -"label.userdatal2": "Gebruiker Data", +"label.user.data": "Gebruiker Data", "label.username": "Gebruikersnaam", "label.users": "Gebruikers", "label.utilization": "Utilisation", @@ -1330,7 +1328,7 @@ "message.confirm.archive.selected.alerts": "bevestig dat u de geselecteerde meldingen wilt archiveren, alstublieft", "message.confirm.archive.selected.events": "bevestig dat u de geselecteerde gebeurtenissen wilt archiveren, alstublieft", "message.confirm.attach.disk": "Weet U zeker dat U een disk wilt koppelen?", -"message.confirm.delete.acl.list": "Weet U zeker dat U dit ACL wilt verwijderen?", +"message.confirm.delete.acl": "Weet U zeker dat U dit ACL wilt verwijderen?", "message.confirm.delete.bigswitchbcf": "bevestig dat u deze BigSwitch BCF Controller wilt verwijderen, alstublieft", "message.confirm.delete.brocadevcs": "bevestigd dat Brocade Vcs Switch wilt verwijderen, altublieft", "message.confirm.delete.ciscoasa1000v": "bevestig dat u CiscoASA100v wilt verwijderen, alstublieft", @@ -1406,7 +1404,7 @@ "message.guest.traffic.in.basic.zone": "Gast netwerk verkeer is communicatie tussen virtuele machines van de eindgebruiker. Specificeer een range van IP adressen welke CloudStack kan uitdelen aan gast VMs. Let erop dat deze range niet overlapt met de gereserveerde systeem IP range.", "message.host.dedicated": "Host toegewijd", "message.host.dedication.released": "Toegewijde host losgekoppeld", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is een software platform welke computer capaciteit herenigd om public, private en hybrid infrastructure as a Service (IaaS) clouds te bouwen. CloudStack™ beheert het netwerk, de opslag en de computer nodes welke de cloud infrastructuur vertegenwoordigen. Gebruik CloudStack™ om cloud computing omgevingen uit te rollen, te beheren en te configureren.

CloudStack™ gaat veel verder dan het draaien van virtuele machine bestanden op commodity hardware, het is een turnkey oplossing om virtuele datacenters (as a service) te realiseren. Daarbij levert het alle essenti\u00eble componenten om multi-tier en multi-tentant cloud applicaties te bouwen en te beheren. Er is een zowel een open-source als Premium versie beschikbaar, waarbij de open-source versie nagenoeg dezelfde functionaliteit biedt als de Premium versie.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is een software platform welke computer capaciteit herenigd om public, private en hybrid infrastructure as a Service (IaaS) clouds te bouwen. CloudStack™ beheert het netwerk, de opslag en de computer nodes welke de cloud infrastructuur vertegenwoordigen. Gebruik CloudStack™ om cloud computing omgevingen uit te rollen, te beheren en te configureren.

CloudStack™ gaat veel verder dan het draaien van virtuele machine bestanden op commodity hardware, het is een turnkey oplossing om virtuele datacenters (as a service) te realiseren. Daarbij levert het alle essenti\u00eble componenten om multi-tier en multi-tentant cloud applicaties te bouwen en te beheren.", "message.installwizard.tooltip.addpod.name": "Een naam voor de pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Dit is de IP range van het private netwerk dat CloudStack gebruikt om met Secundaire Opslag VMs en Console Proxy VMs te communiceren. Deze IP adressen komen uit hetzelfde subnet als de gast VMs.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "De gateway voor de hosts in die pod.", diff --git a/ui/public/locales/pl.json b/ui/public/locales/pl.json index 6838b5e2a59..fbbbf612bc1 100644 --- a/ui/public/locales/pl.json +++ b/ui/public/locales/pl.json @@ -14,12 +14,12 @@ "label.account.specific": "Account-Specific", "label.accounts": "Konta", "label.accounttype": "Account Type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Name", +"label.acl.rule.name": "ACL Name", "label.acquire.new.ip": "Acquire New IP", "label.acquire.new.secondary.ip": "Acquire new secondary IP", "label.action": "Action", @@ -119,8 +119,8 @@ "label.activeviewersessions": "Active Sessions", "label.add": "Dodaj", "label.add.account": "Dodaj konto", -"label.add.acl": "Dodaj ACL", -"label.add.acl.list": "Add ACL List", +"label.add.acl.rule": "Dodaj ACL", +"label.add.acl": "Add ACL rule List", "label.add.affinity.group": "Add new affinity group", "label.add.baremetal.dhcp.device": "Add Baremetal DHCP Device", "label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller", @@ -142,12 +142,12 @@ "label.add.ip.range": "Add IP Range", "label.add.isolated.network": "Add Isolated Network", "label.add.ldap.account": "Add LDAP account", -"label.add.list.name": "ACL List Name", +"label.add.acl.name": "ACL Name", "label.add.more": "Dodaj wi\u0119cej", "label.add.netscaler.device": "Add Netscaler device", "label.add.network": "Dodaj sie\u0107", "label.add.network.acl": "Add network ACL", -"label.add.network.acl.list": "Add Network ACL List", +"label.add.network.acl": "Add Network ACL", "label.add.network.offering": "Add network offering", "label.add.new.gateway": "Add new gateway", "label.add.new.tier": "Add new tier", @@ -334,7 +334,7 @@ "label.default.use": "Default Use", "label.default.view": "Widok domy\u015blny", "label.delete": "Usu\u0144", -"label.delete.acl.list": "Delete ACL List", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "Delete Affinity Group", "label.delete.alerts": "Delete alerts", "label.delete.bigswitchbcf": "Remove BigSwitch BCF Controller", @@ -419,7 +419,7 @@ "label.dpd": "Dead Peer Detection", "label.driver": "Driver", "label.edit": "Edytuj", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.project.details": "Zmie\u0144 szczeg\u00f3\u0142y projektu", "label.edit.role": "Edit Role", @@ -924,7 +924,6 @@ "label.remove.vpc.offering": "Remove VPC offering", "label.removing": "Usuwanie", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL List", "label.required": "Wymagane", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires Upgrade", @@ -1150,8 +1149,7 @@ "label.usehttps": "Use HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "U\u017cytkowni", -"label.userdata": "Userdata", -"label.userdatal2": "User Data", +"label.user.data": "User Data", "label.username": "Nazwa u\u017cytkownika", "label.users": "U\u017cytkownicy", "label.utilization": "Utilisation", @@ -1329,7 +1327,7 @@ "message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts", "message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events", "message.confirm.attach.disk": "Are you sure you want to attach disk?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v", @@ -1406,7 +1404,7 @@ "message.guest.traffic.in.basic.zone": "Guest network traffic is communication between end-user virtual machines. Specify a range of IP addresses that CloudStack can assign to guest VMs. Make sure this range does not overlap the reserved system IP range.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications. Both open-source and Premium versions are available, with the open-source version offering nearly identical features.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software platform that pools computing resources to build public, private, and hybrid Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the network, storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ to deploy, manage, and configure cloud computing environments.

Extending beyond individual virtual machine images running on commodity hardware, CloudStack™ provides a turnkey cloud infrastructure software stack for delivering virtual datacenters as a service - delivering all of the essential components to build, deploy, and manage multi-tier and multi-tenant cloud applications.", "message.installwizard.tooltip.addpod.name": "A name for the pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "This is the IP range in the private network that the CloudStack uses to manage Secondary Storage VMs and Console Proxy VMs. These IP addresses are taken from the same subnet as computing servers.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway for the hosts in that pod.", diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json index 9de19db7442..09b0242ef1a 100644 --- a/ui/public/locales/pt_BR.json +++ b/ui/public/locales/pt_BR.json @@ -33,10 +33,10 @@ "label.accounttype": "Tipo de conta", "label.acl.export": "Exportar ACLs", "label.acl.id": "ACL ID", -"label.acl.list.rules": "Lista de regras de ACL", +"label.acl.rules": "Lista de regras de ACL", "label.acl.reason.description": "Motivo para se utilizar a regra.", "label.aclid": "ACL", -"label.aclname": "Nome da ACL", +"label.acl.rule.name": "Nome da ACL", "label.acquire.new.ip": "Adquirir novo IP", "label.acquire.new.secondary.ip": "Adquira um novo IP secund\u00e1rio", "label.acquiring.ip": "Obtendo IP", @@ -171,8 +171,8 @@ "label.activeviewersessions": "Sess\u00f5es ativas", "label.add": "Adicionar", "label.add.account": "Adicionar conta", -"label.add.acl": "Adicionar ACL", -"label.add.acl.list": "Adiciona lista ACL", +"label.add.acl.rule": "Adicionar ACL", +"label.add.acl": "Adiciona lista ACL", "label.add.affinity.group": "Adicionar um grupo de afinidade", "label.add.baremetal.dhcp.device": "Adicionar dispositivo DHCP baremetal", "label.add.bigswitchbcf.device": "Adicionar controlador BigSwitch BCF", @@ -196,12 +196,12 @@ "label.add.isolated.network": "Adiciona rede isolada", "label.add.kubernetes.cluster": "Adicionar cluster Kubernetes", "label.add.ldap.account": "Adicionar conta LDAP", -"label.add.list.name": "Nome da lista ACL", +"label.add.acl.name": "Nome da lista ACL", "label.add.more": "Adicionar mais", "label.add.netscaler.device": "Adicionar dispositivo Netscaler", "label.add.network": "Adicionar rede", "label.add.network.acl": "Adicione ACL de rede", -"label.add.network.acl.list": "Adicionar lista de ACL de rede", +"label.add.network.acl": "Adicionar lista de ACL de rede", "label.add.network.offering": "Adicionar oferta de rede", "label.add.new.gateway": "Adicionar novo gateway", "label.add.new.tier": "Adicionar nova camada", @@ -463,6 +463,8 @@ "label.dashboard": "Dashboard", "label.data.disk": "Disco de dados", "label.data.disk.offering": "Oferta de disco adicional", +"label.data.pool": "Data pool", +"label.data.pool.description": "\u00c9 necess\u00e1rio informar um data pool ao utilizar um Ceph pool com erasure code", "label.date": "Data", "label.day": "Dia", "label.day.of.month": "Dia do m\u00eas", @@ -483,7 +485,7 @@ "label.default.view": "Visualiza\u00e7\u00e3o padr\u00e3o", "label.defaultnetwork": "Rede padr\u00e3o", "label.delete": "Remover", -"label.delete.acl.list": "Apagar lista ACL", +"label.delete.acl": "Apagar lista ACL", "label.delete.affinity.group": "Apagar grupo de afinidade", "label.delete.alerts": "Remover alertas", "label.delete.backup": "Apagar backup", @@ -610,7 +612,7 @@ "label.dynamicscalingenabled": "Escalonamento din\u00e2mico habilitado", "label.dynamicscalingenabled.tooltip": "VM s\u00f3 pode ser dinamicamente escalonada quando o escalonamento din\u00e2mico estiver habilitado no template, oferta de computa\u00e7\u00e3o e nas configura\u00e7\u00e3oes globais", "label.edit": "Editar", -"label.edit.acl.list": "Editar lista ACL", +"label.edit.acl": "Editar lista ACL", "label.edit.acl.rule": "Editar regra ACL", "label.edit.project.details": "Editar detalhes do projeto", "label.edit.project.role": "Editar fun\u00e7\u00e3o do projeto", @@ -1067,7 +1069,7 @@ "label.netscaler.vpx": "NetScaler VPX LoadBalancer", "label.network": "Rede", "label.network.acl": "ACL de rede", -"label.network.acl.lists": "Lista de redes ACL", +"label.network.acls": "Lista de redes ACL", "label.network.addvm": "Adicionar rede para VM", "label.network.desc": "Descri\u00e7\u00e3o de rede", "label.network.domain": "Dom\u00ednio de rede", @@ -1354,7 +1356,6 @@ "label.removed": "Removido", "label.removing": "Removendo", "label.replace.acl": "Substituir ACL", -"label.replace.acl.list": "Substituir lista ACL", "label.report.bug": "Reportar um problema", "label.required": "Obrigat\u00f3rio", "label.requireshvm": "HVM", @@ -1388,6 +1389,7 @@ "label.rolename": "Fun\u00e7\u00e3o", "label.roles": "Fun\u00e7\u00f5es", "label.roletype": "Tipo de fun\u00e7\u00e3o", +"label.rolepermissiontab.searchbar": "Pesquisa de regras", "label.root.certificate": "Certificado ra\u00edz", "label.root.disk.size": "Tamanho do disco ra\u00edz (GB)", "label.rootdisk": "Disco ra\u00edz", @@ -1698,8 +1700,7 @@ "label.usenewdiskoffering": "Substituir a oferta de disco?", "label.user": "Usu\u00e1rio", "label.user.conflict": "Conflito", -"label.userdata": "Dados de usu\u00e1rio", -"label.userdatal2": "Dados de usu\u00e1rio", +"label.user.data": "Dados de usu\u00e1rio", "label.username": "Nome de usu\u00e1rio", "label.users": "Usu\u00e1rios", "label.usersource": "Tipo de usu\u00e1rio", @@ -1975,7 +1976,7 @@ "message.confirm.archive.selected.events": "Por favor confirme que voc\u00ea deseja arquivar os eventos selecionados", "message.confirm.attach.disk": "Voc\u00ea tem certeza que deseja conectar este disco?", "message.confirm.configure.ovs": "Voc\u00ea tem certeza de que quer configurar os Ovs?", -"message.confirm.delete.acl.list": "Voc\u00ea tem certeza que deseja apagar esta lista ACL?", +"message.confirm.delete.acl": "Voc\u00ea tem certeza que deseja apagar esta lista ACL?", "message.confirm.delete.bigswitchbcf": "Por favor, confirme que voc\u00ea deseja deletar este controlador BigSwitch BCF", "message.confirm.delete.brocadevcs": "Por favor confirme que voc\u00ea deseja remover o switch Brocade Vcs", "message.confirm.delete.ciscoasa1000v": "Favor confirmar que voc\u00ea deseja apagar este CiscoASA1000v", @@ -2223,7 +2224,7 @@ "message.host.dedicated": "Host dedicado", "message.host.dedication.released": "Host dedicado liberado", "message.info.cloudian.console": "O Cloudian management console deve abrir em outra janela", -"message.installwizard.copy.whatiscloudstack": "O CloudStack™ \u00e9 uma plataforma de software que agrega recursos computacionais para construir uma Cloud de infraestrutura como servi\u00e7o (IaaS) p\u00fablica, privada ou h\u00edbrida. O CloudStack™ ger\u00eancia a rede, o armazenamento e os recursos computacionais que comp\u00f5em a infraestrutura de cloud. utilize o CloudStack™ para instalar, gerenciar e configurar os ambientes de cloud computing.

Indo al\u00e9m de imagens de m\u00e1quinas virtuais individuais rodando em hardware commodity, CloudStack™ prov\u00ea uma solu\u00e7\u00e3o completa de software de infraestrutura de cloud para entregar datacenters virtuais como um servi\u00e7o - possuindo todos os componentes essenciais para contruir, instalar e gerenciar aplica\u00e7\u00f5es na cloud multi-camadas e multi-tenant. Ambas as vers\u00f5es open-source e premium est\u00e3o dispon\u00edveis, com a vers\u00e3o opensource oferecendo praticamente os mesmos recursos.", +"message.installwizard.copy.whatiscloudstack": "O CloudStack™ \u00e9 uma plataforma de software que agrega recursos computacionais para construir uma Cloud de infraestrutura como servi\u00e7o (IaaS) p\u00fablica, privada ou h\u00edbrida. O CloudStack™ ger\u00eancia a rede, o armazenamento e os recursos computacionais que comp\u00f5em a infraestrutura de cloud. utilize o CloudStack™ para instalar, gerenciar e configurar os ambientes de cloud computing.

Indo al\u00e9m de imagens de m\u00e1quinas virtuais individuais rodando em hardware commodity, CloudStack™ prov\u00ea uma solu\u00e7\u00e3o completa de software de infraestrutura de cloud para entregar datacenters virtuais como um servi\u00e7o - possuindo todos os componentes essenciais para contruir, instalar e gerenciar aplica\u00e7\u00f5es na cloud multi-camadas e multi-tenant.", "message.installwizard.tooltip.addpod.name": "O nome para o pod", "message.installwizard.tooltip.addpod.reservedsystemendip": "Este \u00e9 o range de IP na rede privada que o CloudStack utiliza para gerenciar o armazenamento secund\u00e1rio das VMs e proxy console das VMs. estes endere\u00e7os IP s\u00e3o obtidos da mesma subrede dos servidores hosts.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "O gateway para os hosts neste pod.", @@ -2390,6 +2391,7 @@ "message.success.change.affinity.group": "Grupos de afinidade alterados com sucesso", "message.success.change.offering": "Oferta alterada com sucesso", "message.success.change.password": "Senha alterada com sucesso", +"message.success.change.host.password": "Senha do host \"{name}\" foi alterada com sucesso", "message.success.config.backup.schedule": "Agendamento de backup de VM configurado com sucesso", "message.success.config.sticky.policy": "Sticky policy configurada com sucesso", "message.success.copy.clipboard": "Copiado com sucesso para a \u00e1rea de transfer\u00eancia", @@ -2507,7 +2509,6 @@ "message.volume.state.uploaderror": "O carregamento do volume encontrou um erro", "message.volume.state.uploadinprogress": "Carregamento do volume em progresso", "message.volume.state.uploadop": "A opera\u00e7\u00e3o de carregamento de volume est\u00e1 em andamento", -"message.vr.alert.upon.network.offering.creation.l2": "Como VRs n\u00e3o s\u00e3o criados para redes do tipo L2, a oferta de computa\u00e7\u00e3o n\u00e3o ser\u00e1 utilizada.", "message.vr.alert.upon.network.offering.creation.others": "Como nenhum dos servi\u00e7os obrigat\u00f3rios para cria\u00e7\u00e3o do VR (VPN, DHCP, DNS, Firewall, LB, UserData, SourceNat, StaticNat, PortForwarding) foram habilitados, o VR n\u00e3o ser\u00e1 criado e a oferta de computa\u00e7\u00e3o n\u00e3o ser\u00e1 usada.", "message.warn.filetype": "jpg, jpeg, png, bmp e svg s\u00e3o os \u00fanicos formatos de imagem suportados", "message.warn.importing.instance.without.nic": "AVISO: essa inst\u00e2ncia est\u00e1 sendo importada sem NICs e muitos recursos de rede n\u00e3o estar\u00e3o dispon\u00edveis. Considere criar uma NIC antes de importar via VCenter ou assim que a inst\u00e2ncia for importada.", diff --git a/ui/public/locales/ru_RU.json b/ui/public/locales/ru_RU.json index f68d767d762..4fb3788bc51 100644 --- a/ui/public/locales/ru_RU.json +++ b/ui/public/locales/ru_RU.json @@ -14,12 +14,12 @@ "label.account.specific": "\u0421\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430 \u0430\u043a\u043a\u0430\u0443\u043d\u043d\u0442\u0430", "label.accounts": "\u0423\u0447\u0451\u0442\u043d\u044b\u0435 \u0437\u0430\u043f\u0438\u0441\u0438", "label.accounttype": "Account Type", -"label.acl.export": "Export ACLs", +"label.acl.export": "Export ACL rules", "label.acl.id": "ACL ID", -"label.acl.list.rules": "ACL List Rules", +"label.acl.rules": "ACL Rules", "label.acl.reason.description": "Enter the reason behind an ACL rule.", "label.aclid": "ACL", -"label.aclname": "ACL Name", +"label.acl.rule.name": "ACL Name", "label.acquire.new.ip": "\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 IP", "label.acquire.new.secondary.ip": "\u0417\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 IP-\u0430\u0434\u0440\u0435\u0441", "label.action": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f", @@ -119,8 +119,8 @@ "label.activeviewersessions": "\u0410\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u0441\u0435\u0441\u0441\u0438\u0438", "label.add": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c", "label.add.account": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0447\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u044c", -"label.add.acl": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c ACL", -"label.add.acl.list": "Add ACL List", +"label.add.acl.rule": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c ACL", +"label.add.acl": "Add ACL", "label.add.affinity.group": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e affinity group", "label.add.baremetal.dhcp.device": "Add Baremetal DHCP Device", "label.add.bigswitchbcf.device": "Add BigSwitch BCF Controller", @@ -142,12 +142,12 @@ "label.add.ip.range": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0430\u0434\u0440\u0435\u0441\u043e\u0432", "label.add.isolated.network": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0437\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0441\u0435\u0442\u044c", "label.add.ldap.account": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c LDAP \u0430\u043a\u043a\u0430\u0443\u043d\u0442", -"label.add.list.name": "ACL List Name", +"label.add.acl.name": "ACL Name", "label.add.more": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0435\u0449\u0435", "label.add.netscaler.device": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c Netscaler \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e", "label.add.network": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0442\u044c", "label.add.network.acl": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u0443\u044e ACL", -"label.add.network.acl.list": "Add Network ACL List", +"label.add.network.acl": "Add Network ACL", "label.add.network.offering": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b", "label.add.new.gateway": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0448\u043b\u044e\u0437", "label.add.new.tier": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 Tier", @@ -334,7 +334,7 @@ "label.default.use": "\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", "label.default.view": "\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0432\u0438\u0434", "label.delete": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c", -"label.delete.acl.list": "Delete ACL List", +"label.delete.acl": "Delete ACL", "label.delete.affinity.group": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c affinity group", "label.delete.alerts": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u0440\u0435\u0432\u043e\u0433\u0438", "label.delete.bigswitchbcf": "Remove BigSwitch BCF Controller", @@ -418,7 +418,7 @@ "label.dpd": "Dead Peer Detection", "label.driver": "Driver", "label.edit": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c", -"label.edit.acl.list": "Edit ACL List", +"label.edit.acl": "Edit ACL", "label.edit.acl.rule": "Edit ACL rule", "label.edit.project.details": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430", "label.edit.role": "Edit Role", @@ -923,7 +923,6 @@ "label.remove.vpc.offering": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0443\u0441\u043b\u0443\u0433\u0443 VPC", "label.removing": "\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435", "label.replace.acl": "Replace ACL", -"label.replace.acl.list": "Replace ACL List", "label.required": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f", "label.requireshvm": "HVM", "label.requiresupgrade": "Requires Upgrade", @@ -1149,8 +1148,7 @@ "label.usehttps": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 HTTPS", "label.usenewdiskoffering": "Replace disk offering?", "label.user": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c", -"label.userdata": "Userdata", -"label.userdatal2": "User Data", +"label.user.data": "User Data", "label.username": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "label.users": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438", "label.utilization": "Utilisation", @@ -1328,7 +1326,7 @@ "message.confirm.archive.selected.alerts": "Please confirm you would like to archive the selected alerts", "message.confirm.archive.selected.events": "Please confirm you would like to archive the selected events", "message.confirm.attach.disk": "Are you sure you want to attach disk?", -"message.confirm.delete.acl.list": "Are you sure you want to delete this ACL list?", +"message.confirm.delete.acl": "Are you sure you want to delete this ACL?", "message.confirm.delete.bigswitchbcf": "Please confirm that you would like to delete this BigSwitch BCF Controller", "message.confirm.delete.brocadevcs": "Please confirm that you would like to delete Brocade Vcs Switch", "message.confirm.delete.ciscoasa1000v": "Please confirm you want to delete CiscoASA1000v", @@ -1404,7 +1402,7 @@ "message.guest.traffic.in.basic.zone": "\u0413\u043e\u0441\u0442\u0435\u0432\u043e\u0439 \u0442\u0440\u0430\u0444\u0438\u043a \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u043e\u0431\u0449\u0435\u043d\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u043c\u0430\u0448\u0438\u043d\u0430\u043c\u0438. \u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u0430\u0434\u0440\u0435\u0441\u043e\u0432 IP, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 CloudStack \u0441\u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0434\u043b\u044f \u0412\u041c. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u044d\u0442\u043e\u0442 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d \u043d\u0435 \u043f\u0435\u0440\u0435\u043a\u0440\u0435\u0449\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u043e\u043c \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0445 \u0430\u0434\u0440\u0435\u0441\u043e\u0432.", "message.host.dedicated": "Host Dedicated", "message.host.dedication.released": "Host dedication released", -"message.installwizard.copy.whatiscloudstack": "CloudStack™ - \u044d\u0442\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u0430\u044f \u043f\u043b\u0430\u0444\u0442\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0445, \u0447\u0430\u0441\u0442\u043d\u044b\u0445 \u0438 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u043a\u043e\u0432 \u043f\u043e \u0441\u0445\u0435\u043c\u0435 \u00ab\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441\u00bb (IaaS). CloudStack™ \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0442\u044c\u044e, \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c \u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0443\u0437\u043b\u0430\u043c\u0438, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u0413\u043b\u0430\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, CloudStack™ \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439.

CloudStack™ \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0443\u0441\u043b\u0443\u0433\u0438 \u0446\u0435\u043b\u043e\u0433\u043e \u0446\u0435\u043d\u0442\u0440\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u043c\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u043b\u0430\u043a\u0430. \u041c\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043c\u0435\u0436\u0434\u0443 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e\u0439 \u0438 \u0411\u0435\u0437\u043d\u0435\u0441-\u0432\u0435\u0440\u0441\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u0447\u0435\u043c \u043d\u0435 \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f.", +"message.installwizard.copy.whatiscloudstack": "CloudStack™ - \u044d\u0442\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u0430\u044f \u043f\u043b\u0430\u0444\u0442\u043e\u0440\u043c\u0430 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0443\u0431\u043b\u0438\u0447\u043d\u044b\u0445, \u0447\u0430\u0441\u0442\u043d\u044b\u0445 \u0438 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u043a\u043e\u0432 \u043f\u043e \u0441\u0445\u0435\u043c\u0435 \u00ab\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043a\u0430\u043a \u0441\u0435\u0440\u0432\u0438\u0441\u00bb (IaaS). CloudStack™ \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0442\u044c\u044e, \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c \u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438 \u0443\u0437\u043b\u0430\u043c\u0438, \u0432\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0432 \u043e\u0431\u043b\u0430\u0447\u043d\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443. \u0413\u043b\u0430\u0432\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, CloudStack™ \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u0432\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f, \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439.

CloudStack™ \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u0443\u0441\u043b\u0443\u0433\u0438 \u0446\u0435\u043b\u043e\u0433\u043e \u0446\u0435\u043d\u0442\u0440\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u043c\u0438 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u043c\u0438 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043e\u0431\u043b\u0430\u043a\u0430.", "message.installwizard.tooltip.addpod.name": "\u0418\u043c\u044f \u0441\u0442\u0435\u043d\u0434\u0430", "message.installwizard.tooltip.addpod.reservedsystemendip": "\u042d\u0442\u043e \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d IP \u0447\u0430\u0441\u0442\u043d\u043e\u0439 \u0441\u0435\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f CloudStack \u0434\u043b\u044f \u0412\u041c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430 \u0438 \u043a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u043a\u0441\u0438. \u042d\u0442\u0438 \u0430\u0434\u0440\u0435\u0441\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442\u0441\u044f \u0438\u0437 \u0441\u0435\u0442\u0438 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432.", "message.installwizard.tooltip.addpod.reservedsystemgateway": "\u0428\u043b\u044e\u0437 \u0434\u043b\u044f \u0443\u0437\u043b\u043e\u0432 \u044d\u0442\u043e\u0433\u043e \u0441\u0442\u0435\u043d\u0434\u0430.", diff --git a/ui/public/locales/te.json b/ui/public/locales/te.json index c6c49529054..957aed4ee88 100644 --- a/ui/public/locales/te.json +++ b/ui/public/locales/te.json @@ -3661,7 +3661,6 @@ "message.volumes.managed": "CloudStack ద్వారా నియంత్రించబడే వాల్యూమ్‌లు.", "message.volumes.unmanaged": "CloudStack ద్వారా వాల్యూమ్‌లు నియంత్రించబడవు.", "message.vpc.restart.required": "VPC(లు) కోసం పునఃప్రారంభించాల్సిన అవసరం ఉంది. ", - "message.vr.alert.upon.network.offering.creation.l2": "L2 నెట్‌వర్క్‌ల కోసం వర్చువల్ రూటర్‌లు సృష్టించబడనందున, కంప్యూట్ ఆఫర్ ఉపయోగించబడదు.", "message.vr.alert.upon.network.offering.creation.others": "వర్చువల్ రూటర్‌ను (VPN, DHCP, DNS, Firewall, LB, UserData, SourceNat, StaticNat, PortForwarding) సృష్టించడం కోసం తప్పనిసరి సేవలు ఏవీ ప్రారంభించబడనందున, వర్చువల్ రూటర్ సృష్టించబడదు మరియు గణన సమర్పణ ఉపయోగించబడదు.", "message.warn.change.primary.storage.scope": "ఈ ఫీచర్ క్రింది కాన్ఫిగరేషన్‌ల కోసం పరీక్షించబడింది మరియు మద్దతు ఇస్తుంది:
KVM - NFS/Ceph - డిఫాల్ట్ ప్రైమరీ
VMware - NFS - డిఫాల్ట్ ప్రైమరీ
*ఇతర కాన్ఫిగరేషన్‌ల కోసం ఇది పని చేయడానికి అదనపు దశలు ఉండవచ్చు.", "message.warn.filetype": "jpg, jpeg, png, bmp మరియు svg మాత్రమే మద్దతు ఉన్న ఇమేజ్ ఫార్మాట్‌లు.", diff --git a/ui/public/locales/zh_CN.json b/ui/public/locales/zh_CN.json index 5337829b53d..d09f960edf5 100644 --- a/ui/public/locales/zh_CN.json +++ b/ui/public/locales/zh_CN.json @@ -66,12 +66,12 @@ "label.acl.export": "\u5BFC\u51FA ACL \u89C4\u5219", "label.acl.id": "ACL \u6807\u8BC6\u7801", - "label.acl.list.rules": "ACL\u5217\u8868\u7B56\u7565", + "label.acl.rules": "ACL\u5217\u8868\u7B56\u7565", "label.acl.reason.description": "\u8F93\u5165\u5B9A\u4E49 ACL \u7B56\u7565\u7684\u539F\u56E0\u3002", "label.acl.replaced": "ACL \u5DF2\u66FF\u6362", "label.aclid": "\u8BBF\u95EE\u63A7\u5236\uFF08ACL\uFF09", - "label.aclname": "ACL \u540D\u79F0", + "label.acl.rule.name": "ACL \u540D\u79F0", "label.acltotal": "\u7F51\u7EDC ACL \u603B\u6570", "label.acquire.new.ip": "\u83B7\u53D6\u65B0 IP \u5730\u5740", "label.acquire.new.secondary.ip": "\u83B7\u53D6\u65B0\u4E8C\u7EA7 IP \u5730\u5740", @@ -336,8 +336,8 @@ "label.add.account": "\u6DFB\u52A0\u5E10\u6237", "label.add.accounts": "\u6DFB\u52A0\u5E10\u6237", "label.add.accounts.to": "\u6DFB\u52A0\u5E10\u6237\u81F3", - "label.add.acl": "\u6DFB\u52A0 ACL", - "label.add.acl.list": "\u6DFB\u52A0 ACL \u5217\u8868", + "label.add.acl.rule": "\u6DFB\u52A0 ACL", + "label.add.acl": "\u6DFB\u52A0 ACL \u5217\u8868", "label.add.affinity.group": "\u6DFB\u52A0\u65B0\u5173\u8054\u6027\u7EC4", "label.add.baremetal.dhcp.device": "\u6DFB\u52A0\u88F8\u673A DHCP \u8BBE\u5907", @@ -381,7 +381,7 @@ "label.add.l2.guest.network": "\u6DFB\u52A0 L2 \u6765\u5BBE\u7F51\u7EDC", "label.add.ldap.account": "\u6DFB\u52A0 LDAP \u8D26\u6237", "label.add.ldap.list.users": "\u5217\u51FA LDAP \u7528\u6237", - "label.add.list.name": "ACL \u5217\u8868\u540D\u79F0", + "label.add.acl.name": "ACL \u5217\u8868\u540D\u79F0", "label.add.load.balancer": "\u6DFB\u52A0\u8D1F\u8F7D\u5747\u8861\u5668", "label.add.management.ip.range": "\u6DFB\u52A0\u7BA1\u7406 IP \u5730\u5740\u8303\u56F4", @@ -389,7 +389,7 @@ "label.add.netscaler.device": "\u6DFB\u52A0 Netscaler \u8BBE\u5907", "label.add.network": "\u6DFB\u52A0\u7F51\u7EDC", "label.add.network.acl": "\u6DFB\u52A0\u7F51\u7EDC ACL", - "label.add.network.acl.list": "\u6DFB\u52A0\u7F51\u7EDC ACL \u5217\u8868", + "label.add.network.acl": "\u6DFB\u52A0\u7F51\u7EDC ACL \u5217\u8868", "label.add.network.device": "\u6DFB\u52A0\u7F51\u7EDC\u8BBE\u5907", "label.add.network.offering": "\u6DFB\u52A0\u7F51\u7EDC\u65B9\u6848", @@ -829,7 +829,7 @@ "label.defaultnetwork": "\u9ED8\u8BA4\u7F51\u7EDC", "label.delete": "\u5220\u9664", - "label.delete.acl.list": "\u5220\u9664 ACL \u5217\u8868", + "label.delete.acl": "\u5220\u9664 ACL \u5217\u8868", "label.delete.affinity.group": "\u5220\u9664\u5173\u8054\u6027\u7EC4", "label.delete.alerts": "\u5220\u9664\u8B66\u62A5", "label.delete.backup": "\u5220\u9664\u5907\u4EFD", @@ -1007,7 +1007,7 @@ "label.computeonly.offering.tooltip": "\u5728\u8BA1\u7B97\u65B9\u6848\u4E2D\u6307\u5B9A\u4E0E\u6839\u78C1\u76D8\u76F8\u5173\u7684\u4FE1\u606F\u6216\u5C06\u78C1\u76D8\u65B9\u6848\u76F4\u63A5\u94FE\u63A5\u5230\u8BA1\u7B97\u65B9\u6848\u7684\u9009\u9879", "label.edit": "\u7F16\u8F91", - "label.edit.acl.list": "\u7F16\u8F91 ACL \u5217\u8868", + "label.edit.acl": "\u7F16\u8F91 ACL \u5217\u8868", "label.edit.acl.rule": "\u7F16\u8F91 ACL \u89C4\u5219", "label.edit.affinity.group": "\u7F16\u8F91\u5173\u8054\u6027\u7EC4", "label.edit.lb.rule": "\u7F16\u8F91\u8D1F\u8F7D\u5747\u8861\u5668\u89C4\u5219", @@ -1717,7 +1717,7 @@ "label.netscaler.vpx": "NetScaler VPX \u8D1F\u8F7D\u5747\u8861\u5668", "label.network": "\u7F51\u7EDC", "label.network.acl": "\u7F51\u7EDC ACL", - "label.network.acl.lists": "\u7F51\u7EDC ACL \u5217\u8868", + "label.network.acls": "\u7F51\u7EDC ACL \u5217\u8868", "label.network.acls": "\u7F51\u7EDC ACL", "label.network.addvm": "\u5C06\u7F51\u7EDC\u6DFB\u52A0\u5230\u865A\u62DF\u673A", "label.network.desc": "\u7F51\u7EDC\u63CF\u8FF0", @@ -2136,7 +2136,6 @@ "label.removing.user": "\u6B63\u5728\u5220\u9664\u7528\u6237", "label.replace.acl": "\u66FF\u6362 ACL", - "label.replace.acl.list": "\u66FF\u6362 ACL \u5217\u8868", "label.report.bug": "\u62A5\u544A\u95EE\u9898", "label.required": "\u5FC5\u586B\u9879", @@ -2666,8 +2665,7 @@ "label.user.details": "\u7528\u6237\u8BE6\u60C5", "label.user.source": "\u6765\u6E90", "label.user.vm": "\u7528\u6237\u865A\u62DF\u673A", - "label.userdata": "\u7528\u6237\u6570\u636E", - "label.userdatal2": "\u7528\u6237\u6570\u636E", + "label.user.data": "\u7528\u6237\u6570\u636E", "label.username": "\u7528\u6237\u540D", "label.users": "\u7528\u6237", "label.usersource": "\u7528\u6237\u7C7B\u578B", @@ -3129,7 +3127,7 @@ "message.confirm.dedicate.pod.domain.account": "\u662F\u5426\u786E\u5B9E\u8981\u5C06\u6B64\u63D0\u4F9B\u70B9\u4E13\u7528\u4E8E\u57DF/\u5E10\u6237\uFF1F", "message.confirm.dedicate.zone": "\u662F\u5426\u8981\u5C06\u6B64\u8D44\u6E90\u57DF\u4E13\u7528\u4E8E\u57DF/\u5E10\u6237\uFF1F", - "message.confirm.delete.acl.list": "\u662F\u5426\u786E\u5B9E\u8981\u5220\u9664\u6B64 ACL \u5217\u8868\uFF1F", + "message.confirm.delete.acl": "\u662F\u5426\u786E\u5B9E\u8981\u5220\u9664\u6B64 ACL \u5217\u8868\uFF1F", "message.confirm.delete.alert": "\u662F\u5426\u786E\u5B9E\u8981\u5220\u9664\u6B64\u8B66\u62A5\uFF1F", "message.confirm.delete.baremetal.rack.configuration": "\u8BF7\u786E\u8BA4\u60A8\u786E\u5B9E\u8981\u5220\u9664 Baremetal Rack \u914D\u7F6E", "message.confirm.delete.bigswitchbcf": "\u8BF7\u786E\u8BA4\u60A8\u786E\u5B9E\u8981\u5220\u9664\u6B64 BigSwitch BCF \u63A7\u5236\u5668", @@ -3498,7 +3496,7 @@ "message.installwizard.copy.whatisahost": "\u4E3B\u673A\u662F\u6307\u4E00\u53F0\u8BA1\u7B97\u673A\u3002\u4E3B\u673A\u63D0\u4F9B\u8FD0\u884C\u6765\u5BBE\u865A\u62DF\u673A\u7684\u8BA1\u7B97\u8D44\u6E90\u3002\u6BCF\u53F0\u4E3B\u673A\u4E0A\u90FD\u5B89\u88C5\u6709\u865A\u62DF\u673A\u7BA1\u7406\u7A0B\u5E8F\u8F6F\u4EF6\uFF0C\u7528\u4E8E\u7BA1\u7406\u6765\u5BBE\u865A\u62DF\u673A\uFF08\u88F8\u673A\u4E3B\u673A\u9664\u5916\uFF0C\u5C06\u5728\u201C\u9AD8\u7EA7\u5B89\u88C5\u6307\u5357\u201D\u4E2D\u8BA8\u8BBA\u8FD9\u4E00\u7279\u6B8A\u6848\u4F8B\uFF09\u3002\u4F8B\u5982\uFF0C\u542F\u7528\u4E86 KVM \u7684 Linux \u670D\u52A1\u5668\u3001Citrix XenServer \u670D\u52A1\u5668\u548C ESXi \u670D\u52A1\u5668\u90FD\u53EF\u7528\u4F5C\u4E3B\u673A\u3002\u5728\u57FA\u672C\u5B89\u88C5\u4E2D\uFF0C\u6211\u4EEC\u5C06\u4F7F\u7528\u4E00\u53F0\u8FD0\u884C XenServer \u7684\u4E3B\u673A\u3002

\u4E3B\u673A\u662F CloudStack\u2122 \u90E8\u7F72\u4E2D\u6700\u5C0F\u7684\u7EC4\u7EC7\u5355\u4F4D\u3002\u4E3B\u673A\u5305\u542B\u5728\u96C6\u7FA4\u4E2D\uFF0C\u96C6\u7FA4\u5305\u542B\u5728\u63D0\u4F9B\u70B9\u4E2D\uFF0C\u63D0\u4F9B\u70B9\u5305\u542B\u5728\u8D44\u6E90\u57DF\u4E2D\u3002", "message.installwizard.copy.whatisapod": "\u4E00\u4E2A\u63D0\u4F9B\u70B9\u901A\u5E38\u4EE3\u8868\u4E00\u4E2A\u673A\u67B6\u3002\u540C\u4E00\u63D0\u4F9B\u70B9\u4E2D\u7684\u4E3B\u673A\u4F4D\u4E8E\u540C\u4E00\u5B50\u7F51\u4E2D\u3002

\u63D0\u4F9B\u70B9\u662F CloudStack\u2122 \u90E8\u7F72\u4E2D\u7684\u7B2C\u4E8C\u5927\u7EC4\u7EC7\u5355\u4F4D\u3002\u63D0\u4F9B\u70B9\u5305\u542B\u5728\u8D44\u6E90\u57DF\u4E2D\u3002\u6BCF\u4E2A\u8D44\u6E90\u57DF\u4E2D\u53EF\u4EE5\u5305\u542B\u4E00\u4E2A\u6216\u591A\u4E2A\u63D0\u4F9B\u70B9\uFF1B\u5728\u57FA\u672C\u5B89\u88C5\u4E2D\uFF0C\u60A8\u7684\u8D44\u6E90\u57DF\u4E2D\u5C06\u4EC5\u5305\u542B\u4E00\u4E2A\u63D0\u4F9B\u70B9\u3002", "message.installwizard.copy.whatisazone": "\u8D44\u6E90\u57DF\u662F CloudStack\u2122 \u90E8\u7F72\u4E2D\u6700\u5927\u7684\u7EC4\u7EC7\u5355\u4F4D\u3002\u867D\u7136\u5141\u8BB8\u4E00\u4E2A\u6570\u636E\u4E2D\u5FC3\u4E2D\u5B58\u5728\u591A\u4E2A\u8D44\u6E90\u57DF\uFF0C\u4F46\u662F\u4E00\u4E2A\u8D44\u6E90\u57DF\u901A\u5E38\u4E0E\u4E00\u4E2A\u6570\u636E\u4E2D\u5FC3\u76F8\u5BF9\u5E94\u3002\u5C06\u57FA\u7840\u67B6\u6784\u7F16\u7EC4\u5230\u8D44\u6E90\u57DF\u4E2D\u7684\u597D\u5904\u662F\u53EF\u4EE5\u63D0\u4F9B\u7269\u7406\u9694\u79BB\u548C\u5197\u4F59\u3002\u4F8B\u5982\uFF0C\u6BCF\u4E2A\u8D44\u6E90\u57DF\u90FD\u53EF\u4EE5\u62E5\u6709\u5404\u81EA\u7684\u7535\u6E90\u4F9B\u5E94\u548C\u7F51\u7EDC\u4E0A\u884C\u65B9\u6848\uFF0C\u5E76\u4E14\u5404\u8D44\u6E90\u57DF\u53EF\u4EE5\u5728\u5730\u7406\u4F4D\u7F6E\u4E0A\u76F8\u9694\u5F88\u8FDC\uFF08\u867D\u7136\u5E76\u975E\u5FC5\u987B\u76F8\u9694\u5F88\u8FDC\uFF09\u3002", - "message.installwizard.copy.whatiscloudstack": "CloudStack\u2122 \u662F\u4E00\u4E2A\u8F6F\u4EF6\u5E73\u53F0\uFF0C\u53EF\u5C06\u8BA1\u7B97\u8D44\u6E90\u96C6\u4E2D\u5728\u4E00\u8D77\u4EE5\u6784\u5EFA\u516C\u6709\u3001\u79C1\u6709\u548C\u6DF7\u5408\u57FA\u7840\u8BBE\u65BD\u5373\u670D\u52A1\uFF08IaaS\uFF09\u4E91\u3002 CloudStack\u2122 \u8D1F\u8D23\u7BA1\u7406\u7EC4\u6210\u4E91\u57FA\u7840\u67B6\u6784\u7684\u7F51\u7EDC\u3001\u5B58\u50A8\u548C\u8BA1\u7B97\u8282\u70B9\u3002\u4F7F\u7528 CloudStack\u2122 \u53EF\u4EE5\u90E8\u7F72\u3001\u7BA1\u7406\u548C\u914D\u7F6E\u4E91\u8BA1\u7B97\u73AF\u5883\u3002

CloudStack\u2122 \u901A\u8FC7\u6269\u5C55\u5546\u7528\u786C\u4EF6\u4E0A\u8FD0\u884C\u7684\u6BCF\u4E2A\u865A\u62DF\u673A\u6620\u50CF\u7684\u8303\u56F4\uFF0C\u63D0\u4F9B\u4E86\u4E00\u4E2A\u5B9E\u65F6\u53EF\u7528\u7684\u4E91\u57FA\u7840\u67B6\u6784\u8F6F\u4EF6\u5806\u6808\u7528\u4E8E\u4EE5\u670D\u52A1\u65B9\u5F0F\u4EA4\u4ED8\u865A\u62DF\u6570\u636E\u4E2D\u5FC3\uFF0C\u5373\u4EA4\u4ED8\u6784\u5EFA\u3001\u90E8\u7F72\u548C\u7BA1\u7406\u591A\u5C42\u6B21\u548C\u591A\u79DF\u6237\u4E91\u5E94\u7528\u7A0B\u5E8F\u5FC5\u9700\u7684\u6240\u6709\u7EC4\u4EF6\u3002\u5F00\u6E90\u7248\u672C\u548C Premium \u7248\u672C\u90FD\u5DF2\u53EF\u7528\uFF0C\u4E14\u63D0\u4F9B\u7684\u529F\u80FD\u51E0\u4E4E\u5B8C\u5168\u76F8\u540C\u3002", + "message.installwizard.copy.whatiscloudstack": "CloudStack\u2122 \u662F\u4E00\u4E2A\u8F6F\u4EF6\u5E73\u53F0\uFF0C\u53EF\u5C06\u8BA1\u7B97\u8D44\u6E90\u96C6\u4E2D\u5728\u4E00\u8D77\u4EE5\u6784\u5EFA\u516C\u6709\u3001\u79C1\u6709\u548C\u6DF7\u5408\u57FA\u7840\u8BBE\u65BD\u5373\u670D\u52A1\uFF08IaaS\uFF09\u4E91\u3002 CloudStack\u2122 \u8D1F\u8D23\u7BA1\u7406\u7EC4\u6210\u4E91\u57FA\u7840\u67B6\u6784\u7684\u7F51\u7EDC\u3001\u5B58\u50A8\u548C\u8BA1\u7B97\u8282\u70B9\u3002\u4F7F\u7528 CloudStack\u2122 \u53EF\u4EE5\u90E8\u7F72\u3001\u7BA1\u7406\u548C\u914D\u7F6E\u4E91\u8BA1\u7B97\u73AF\u5883\u3002

CloudStack\u2122 \u901A\u8FC7\u6269\u5C55\u5546\u7528\u786C\u4EF6\u4E0A\u8FD0\u884C\u7684\u6BCF\u4E2A\u865A\u62DF\u673A\u6620\u50CF\u7684\u8303\u56F4\uFF0C\u63D0\u4F9B\u4E86\u4E00\u4E2A\u5B9E\u65F6\u53EF\u7528\u7684\u4E91\u57FA\u7840\u67B6\u6784\u8F6F\u4EF6\u5806\u6808\u7528\u4E8E\u4EE5\u670D\u52A1\u65B9\u5F0F\u4EA4\u4ED8\u865A\u62DF\u6570\u636E\u4E2D\u5FC3\uFF0C\u5373\u4EA4\u4ED8\u6784\u5EFA\u3001\u90E8\u7F72\u548C\u7BA1\u7406\u591A\u5C42\u6B21\u548C\u591A\u79DF\u6237\u4E91\u5E94\u7528\u7A0B\u5E8F\u5FC5\u9700\u7684\u6240\u6709\u7EC4\u4EF6\u3002", "message.installwizard.copy.whatisprimarystorage": "CloudStack\u2122 \u4E91\u57FA\u7840\u67B6\u6784\u4F7F\u7528\u4EE5\u4E0B\u4E24\u79CD\u7C7B\u578B\u7684\u5B58\u50A8: \u4E3B\u5B58\u50A8\u548C\u4E8C\u7EA7\u5B58\u50A8\u3002\u8FD9\u4E24\u79CD\u7C7B\u578B\u7684\u5B58\u50A8\u53EF\u4EE5\u662F iSCSI \u6216 NFS \u670D\u52A1\u5668\uFF0C\u4E5F\u53EF\u4EE5\u662F\u672C\u5730\u78C1\u76D8\u3002

\u4E3B\u5B58\u50A8\u4E0E\u96C6\u7FA4\u76F8\u5173\u8054\uFF0C\u7528\u4E8E\u5B58\u50A8\u8BE5\u96C6\u7FA4\u4E2D\u7684\u4E3B\u673A\u4E0A\u6B63\u5728\u8FD0\u884C\u7684\u6240\u6709\u865A\u62DF\u673A\u5BF9\u5E94\u7684\u6BCF\u4E2A\u6765\u5BBE\u865A\u62DF\u673A\u7684\u78C1\u76D8\u5377\u3002\u4E3B\u5B58\u50A8\u670D\u52A1\u5668\u901A\u5E38\u4F4D\u4E8E\u9760\u8FD1\u4E3B\u673A\u7684\u4F4D\u7F6E\u3002", "message.installwizard.copy.whatissecondarystorage": "\u4E8C\u7EA7\u5B58\u50A8\u4E0E\u8D44\u6E90\u57DF\u76F8\u5173\u8054\uFF0C\u7528\u4E8E\u5B58\u50A8\u4EE5\u4E0B\u9879\u76EE:
  • \u6A21\u677F - \u53EF\u7528\u4E8E\u542F\u52A8\u865A\u62DF\u673A\u5E76\u53EF\u4EE5\u5305\u542B\u5176\u4ED6\u914D\u7F6E\u4FE1\u606F\uFF08\u4F8B\u5982\uFF0C\u5DF2\u5B89\u88C5\u7684\u5E94\u7528\u7A0B\u5E8F\uFF09\u7684\u64CD\u4F5C\u7CFB\u7EDF\u6620\u50CF
  • ISO \u6620\u50CF - \u53EF\u91CD\u65B0\u542F\u52A8\u6216\u4E0D\u53EF\u91CD\u65B0\u542F\u52A8\u7684\u64CD\u4F5C\u7CFB\u7EDF\u6620\u50CF
  • \u78C1\u76D8\u5377\u5FEB\u7167 - \u5DF2\u4FDD\u5B58\u7684\u865A\u62DF\u673A\u6570\u636E\u526F\u672C\uFF0C\u53EF\u7528\u4E8E\u6267\u884C\u6570\u636E\u6062\u590D\u6216\u521B\u5EFA\u65B0\u6A21\u677F
", "message.installwizard.now.building": "\u73B0\u5728\u6B63\u5728\u6784\u5EFA\u60A8\u7684\u4E91...", diff --git a/ui/src/api/index.js b/ui/src/api/index.js index 7ab87780a9d..0c8e8e9696c 100644 --- a/ui/src/api/index.js +++ b/ui/src/api/index.js @@ -23,18 +23,23 @@ import { ACCESS_TOKEN } from '@/store/mutation-types' -export function api (command, args = {}, method = 'GET', data = {}) { - let params = {} +const getAPICommandsRegex = /^(get|list|query|find)\w+$/i +const additionalGetAPICommandsList = [ + 'isaccountallowedtocreateofferingswithtags', + 'readyforshutdown', + 'cloudianisenabled', + 'quotabalance', + 'quotasummary', + 'quotatarifflist', + 'quotaisenabled', + 'quotastatement', + 'verifyoauthcodeandgetuser' +] + +export function getAPI (command, args = {}) { args.command = command args.response = 'json' - if (data) { - params = new URLSearchParams() - Object.entries(data).forEach(([key, value]) => { - params.append(key, value) - }) - } - const sessionkey = vueProps.$localStorage.get(ACCESS_TOKEN) || Cookies.get('sessionkey') if (sessionkey) { args.sessionkey = sessionkey @@ -45,18 +50,46 @@ export function api (command, args = {}, method = 'GET', data = {}) { ...args }, url: '/', - method, - data: params || {} + method: 'GET' }) } +export function postAPI (command, data = {}) { + const params = new URLSearchParams() + params.append('command', command) + params.append('response', 'json') + if (data) { + Object.entries(data).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + params.append(key, value) + } + }) + } + + const sessionkey = vueProps.$localStorage.get(ACCESS_TOKEN) || Cookies.get('sessionkey') + if (sessionkey) { + params.append('sessionkey', sessionkey) + } + return axios({ + url: '/', + method: 'POST', + data: params + }) +} + +export function callAPI (command, args = {}) { + const isGetAPICommand = getAPICommandsRegex.test(command) || additionalGetAPICommandsList.includes(command.toLowerCase()) + const call = isGetAPICommand ? getAPI : postAPI + return call(command, args) +} + export function login (arg) { if (!sourceToken.checkExistSource()) { sourceToken.init() } // Logout before login is called to purge any duplicate sessionkey cookies - api('logout') + postAPI('logout') const params = new URLSearchParams() params.append('command', 'login') @@ -66,7 +99,7 @@ export function login (arg) { params.append('response', 'json') return axios({ url: '/', - method: 'post', + method: 'POST', data: params, headers: { 'content-type': 'application/x-www-form-urlencoded' @@ -74,10 +107,13 @@ export function login (arg) { }) } -export function logout () { - message.destroy() - notification.destroy() - return api('logout') +export async function logout () { + const result = await postAPI('logout').finally(() => { + sourceToken.cancel() + message.destroy() + notification.destroy() + }) + return result } export function oauthlogin (arg) { @@ -86,7 +122,7 @@ export function oauthlogin (arg) { } // Logout before login is called to purge any duplicate sessionkey cookies - api('logout') + postAPI('logout') const params = new URLSearchParams() params.append('command', 'oauthlogin') diff --git a/ui/src/components/CheckBoxSelectPair.vue b/ui/src/components/CheckBoxSelectPair.vue index 4fba1da2556..480e515be29 100644 --- a/ui/src/components/CheckBoxSelectPair.vue +++ b/ui/src/components/CheckBoxSelectPair.vue @@ -21,7 +21,7 @@ {{ checkBoxLabel }} @@ -32,7 +32,7 @@ :label="selectLabel"> + + + + + + + + + + diff --git a/ui/src/components/header/ProjectMenu.vue b/ui/src/components/header/ProjectMenu.vue index 6fb0c3af350..32a77723825 100644 --- a/ui/src/components/header/ProjectMenu.vue +++ b/ui/src/components/header/ProjectMenu.vue @@ -17,112 +17,81 @@ diff --git a/ui/src/components/header/SamlDomainSwitcher.vue b/ui/src/components/header/SamlDomainSwitcher.vue index 1d820dcbcff..e0799bef4d8 100644 --- a/ui/src/components/header/SamlDomainSwitcher.vue +++ b/ui/src/components/header/SamlDomainSwitcher.vue @@ -51,7 +51,7 @@ diff --git a/ui/src/components/view/DedicateData.vue b/ui/src/components/view/DedicateData.vue index d99b080e00d..079ff139141 100644 --- a/ui/src/components/view/DedicateData.vue +++ b/ui/src/components/view/DedicateData.vue @@ -53,7 +53,7 @@ + + + + diff --git a/ui/src/components/view/DetailSettings.vue b/ui/src/components/view/DetailSettings.vue index 9f808b907c7..987a8ac4213 100644 --- a/ui/src/components/view/DetailSettings.vue +++ b/ui/src/components/view/DetailSettings.vue @@ -128,7 +128,7 @@ + + diff --git a/ui/src/components/view/GPUSummaryTab.vue b/ui/src/components/view/GPUSummaryTab.vue new file mode 100644 index 00000000000..52553f143d5 --- /dev/null +++ b/ui/src/components/view/GPUSummaryTab.vue @@ -0,0 +1,294 @@ + +// 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. + + + + + + diff --git a/ui/src/components/view/GPUTab.vue b/ui/src/components/view/GPUTab.vue new file mode 100644 index 00000000000..d2cd2f3d2cb --- /dev/null +++ b/ui/src/components/view/GPUTab.vue @@ -0,0 +1,440 @@ +// 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. + + + + + + diff --git a/ui/src/components/view/ImageDeployInstanceButton.vue b/ui/src/components/view/ImageDeployInstanceButton.vue new file mode 100644 index 00000000000..b2d4b55bc6a --- /dev/null +++ b/ui/src/components/view/ImageDeployInstanceButton.vue @@ -0,0 +1,150 @@ +// 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. + + + + diff --git a/ui/src/components/view/ImageStoreSelectView.vue b/ui/src/components/view/ImageStoreSelectView.vue index 13c5a68e5b1..7871703cd8e 100644 --- a/ui/src/components/view/ImageStoreSelectView.vue +++ b/ui/src/components/view/ImageStoreSelectView.vue @@ -75,7 +75,7 @@ diff --git a/ui/src/components/view/ObjectStoreBrowser.vue b/ui/src/components/view/ObjectStoreBrowser.vue index 531846a9da5..5507540d1f4 100644 --- a/ui/src/components/view/ObjectStoreBrowser.vue +++ b/ui/src/components/view/ObjectStoreBrowser.vue @@ -468,7 +468,8 @@ export default { return false }, uploadFiles () { - if (!this.uploadDirectory.endsWith('/')) { + this.uploadDirectory = this.uploadDirectory.trim() + if (this.uploadDirectory.length !== 0 && !this.uploadDirectory.endsWith('/')) { this.uploadDirectory = this.uploadDirectory + '/' } var promises = [] @@ -488,7 +489,12 @@ export default { asyncUploadFile (file, objectName) { return new Promise((resolve, reject) => { file.arrayBuffer().then((buffer) => { - this.client.putObject(this.resource.name, objectName, Buffer.from(buffer), file.size, this.uploadMetaData, err => { + const metadata = { + ...this.uploadMetaData, + 'Content-Type': file.type || 'binary/octet-stream' + } + + this.client.putObject(this.resource.name, objectName, Buffer.from(buffer), file.size, metadata, err => { if (err) { return reject(this.$notification.error({ message: this.$t('message.upload.failed'), diff --git a/ui/src/components/view/ResourceCountUsage.vue b/ui/src/components/view/ResourceCountUsage.vue index 265c6e78955..688a706948b 100644 --- a/ui/src/components/view/ResourceCountUsage.vue +++ b/ui/src/components/view/ResourceCountUsage.vue @@ -91,7 +91,7 @@ export default { data () { return { usageList: [ - 'vm', 'cpu', 'memory', 'primarystorage', 'volume', 'ip', 'network', + 'vm', 'cpu', 'memory', 'gpu', 'primarystorage', 'volume', 'ip', 'network', 'vpc', 'secondarystorage', 'snapshot', 'template', 'project', 'backup', 'backupstorage', 'bucket', 'objectstorage' ], taggedUsage: {}, @@ -115,6 +115,7 @@ export default { 0: 'vm', 8: 'cpu', 9: 'memory', + 16: 'gpu', 2: 'volume', 10: 'primarystorage' } diff --git a/ui/src/components/view/ResourceIcon.vue b/ui/src/components/view/ResourceIcon.vue index 176c0c7ca8f..8940233fded 100644 --- a/ui/src/components/view/ResourceIcon.vue +++ b/ui/src/components/view/ResourceIcon.vue @@ -16,7 +16,7 @@ // under the License. diff --git a/ui/src/components/view/VmwareData.vue b/ui/src/components/view/VmwareData.vue index efb921df5f3..e978fbb29da 100644 --- a/ui/src/components/view/VmwareData.vue +++ b/ui/src/components/view/VmwareData.vue @@ -35,7 +35,7 @@ + + diff --git a/ui/src/components/widgets/Console.vue b/ui/src/components/widgets/Console.vue index 6c16c7546a7..31105743795 100644 --- a/ui/src/components/widgets/Console.vue +++ b/ui/src/components/widgets/Console.vue @@ -19,7 +19,7 @@ - + @@ -28,7 +28,7 @@ + diff --git a/ui/src/components/widgets/InfiniteScrollSelect.vue b/ui/src/components/widgets/InfiniteScrollSelect.vue new file mode 100644 index 00000000000..c11f9686446 --- /dev/null +++ b/ui/src/components/widgets/InfiniteScrollSelect.vue @@ -0,0 +1,298 @@ +// 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. + + + + + + diff --git a/ui/src/components/widgets/OsLogo.vue b/ui/src/components/widgets/OsLogo.vue index 570cb40f3d5..643953012c1 100644 --- a/ui/src/components/widgets/OsLogo.vue +++ b/ui/src/components/widgets/OsLogo.vue @@ -21,7 +21,7 @@ {{ name }} @@ -29,7 +29,7 @@ + diff --git a/ui/src/views/compute/KubernetesRemoveNodes.vue b/ui/src/views/compute/KubernetesRemoveNodes.vue new file mode 100644 index 00000000000..803ec2bd1b1 --- /dev/null +++ b/ui/src/views/compute/KubernetesRemoveNodes.vue @@ -0,0 +1,151 @@ +// 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. + + + + + diff --git a/ui/src/views/compute/KubernetesServiceTab.vue b/ui/src/views/compute/KubernetesServiceTab.vue index 1c2c83a35ce..fc8f9c60213 100644 --- a/ui/src/views/compute/KubernetesServiceTab.vue +++ b/ui/src/views/compute/KubernetesServiceTab.vue @@ -117,9 +117,15 @@
{{ cksSshPortSharedNetwork }}
-
+
{{ cksSshStartingPort + index }}
+
+ {{ parseInt(etcdSshPort) + parseInt(getEtcdIndex(record.name)) - 1 }} +
+ + + diff --git a/ui/src/views/compute/StartVirtualMachine.vue b/ui/src/views/compute/StartVirtualMachine.vue index f229cb44f67..d85b33ceaed 100644 --- a/ui/src/views/compute/StartVirtualMachine.vue +++ b/ui/src/views/compute/StartVirtualMachine.vue @@ -115,7 +115,7 @@ + + + + diff --git a/ui/src/views/compute/wizard/MultiDiskSelection.vue b/ui/src/views/compute/wizard/MultiDiskSelection.vue index 8344508ad33..f9ae73860e6 100644 --- a/ui/src/views/compute/wizard/MultiDiskSelection.vue +++ b/ui/src/views/compute/wizard/MultiDiskSelection.vue @@ -72,7 +72,7 @@ + + diff --git a/ui/src/views/compute/wizard/OsBasedImageSelection.vue b/ui/src/views/compute/wizard/OsBasedImageSelection.vue new file mode 100644 index 00000000000..6a52eea207b --- /dev/null +++ b/ui/src/views/compute/wizard/OsBasedImageSelection.vue @@ -0,0 +1,375 @@ +// 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. + + + + + + diff --git a/ui/src/views/compute/wizard/OsBasedImageSelectionSearchView.vue b/ui/src/views/compute/wizard/OsBasedImageSelectionSearchView.vue new file mode 100644 index 00000000000..95088d19b5a --- /dev/null +++ b/ui/src/views/compute/wizard/OsBasedImageSelectionSearchView.vue @@ -0,0 +1,119 @@ +// 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. + + + + + + diff --git a/ui/src/views/compute/wizard/OwnershipSelection.vue b/ui/src/views/compute/wizard/OwnershipSelection.vue index a2c7ea4c1f8..59f781a75aa 100644 --- a/ui/src/views/compute/wizard/OwnershipSelection.vue +++ b/ui/src/views/compute/wizard/OwnershipSelection.vue @@ -128,7 +128,7 @@ + + diff --git a/ui/src/views/compute/wizard/ZoneBlockRadioGroupSelect.vue b/ui/src/views/compute/wizard/ZoneBlockRadioGroupSelect.vue new file mode 100644 index 00000000000..2250fc7977d --- /dev/null +++ b/ui/src/views/compute/wizard/ZoneBlockRadioGroupSelect.vue @@ -0,0 +1,51 @@ +// 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. + + + + diff --git a/ui/src/views/dashboard/CapacityDashboard.vue b/ui/src/views/dashboard/CapacityDashboard.vue index 53a3d87aa23..bf25b706306 100644 --- a/ui/src/views/dashboard/CapacityDashboard.vue +++ b/ui/src/views/dashboard/CapacityDashboard.vue @@ -210,14 +210,14 @@ - +
-
+
{{ $t(ts[ctype]) }} @@ -332,7 +332,7 @@ + + diff --git a/ui/src/views/extension/CreateExtension.vue b/ui/src/views/extension/CreateExtension.vue new file mode 100644 index 00000000000..0dfde238279 --- /dev/null +++ b/ui/src/views/extension/CreateExtension.vue @@ -0,0 +1,256 @@ +// 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. + + + + + + diff --git a/ui/src/views/extension/ExtensionCustomActionsTab.vue b/ui/src/views/extension/ExtensionCustomActionsTab.vue new file mode 100644 index 00000000000..a7b6d59c1cf --- /dev/null +++ b/ui/src/views/extension/ExtensionCustomActionsTab.vue @@ -0,0 +1,274 @@ +// 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. + + + + diff --git a/ui/src/views/extension/ExtensionResourcesTab.vue b/ui/src/views/extension/ExtensionResourcesTab.vue new file mode 100644 index 00000000000..c69c79306c8 --- /dev/null +++ b/ui/src/views/extension/ExtensionResourcesTab.vue @@ -0,0 +1,135 @@ +// 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. + + + + diff --git a/ui/src/views/extension/ExternalConfigurationDetails.vue b/ui/src/views/extension/ExternalConfigurationDetails.vue new file mode 100644 index 00000000000..e7b3f298fe2 --- /dev/null +++ b/ui/src/views/extension/ExternalConfigurationDetails.vue @@ -0,0 +1,116 @@ +// 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. + + + + diff --git a/ui/src/views/extension/ParametersInput.vue b/ui/src/views/extension/ParametersInput.vue new file mode 100644 index 00000000000..f77b0471eaa --- /dev/null +++ b/ui/src/views/extension/ParametersInput.vue @@ -0,0 +1,328 @@ +// 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. + + + + + + diff --git a/ui/src/views/extension/RegisterExtension.vue b/ui/src/views/extension/RegisterExtension.vue new file mode 100644 index 00000000000..f856c181f8c --- /dev/null +++ b/ui/src/views/extension/RegisterExtension.vue @@ -0,0 +1,200 @@ +// 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. + + + + + + diff --git a/ui/src/views/extension/RunCustomAction.vue b/ui/src/views/extension/RunCustomAction.vue new file mode 100644 index 00000000000..bd26cf7f375 --- /dev/null +++ b/ui/src/views/extension/RunCustomAction.vue @@ -0,0 +1,386 @@ +// 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. + + + + + + diff --git a/ui/src/views/extension/UpdateCustomAction.vue b/ui/src/views/extension/UpdateCustomAction.vue new file mode 100644 index 00000000000..008882a5238 --- /dev/null +++ b/ui/src/views/extension/UpdateCustomAction.vue @@ -0,0 +1,232 @@ +// 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. + + + + + + diff --git a/ui/src/views/extension/UpdateExtension.vue b/ui/src/views/extension/UpdateExtension.vue new file mode 100644 index 00000000000..192a339b43a --- /dev/null +++ b/ui/src/views/extension/UpdateExtension.vue @@ -0,0 +1,160 @@ +// 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. + + + + + + diff --git a/ui/src/views/iam/AddAccount.vue b/ui/src/views/iam/AddAccount.vue index b39c8b9dab8..9118a61e7fe 100644 --- a/ui/src/views/iam/AddAccount.vue +++ b/ui/src/views/iam/AddAccount.vue @@ -191,7 +191,7 @@ + + diff --git a/ui/src/views/iam/DeleteAccountWrapper.vue b/ui/src/views/iam/DeleteAccountWrapper.vue new file mode 100644 index 00000000000..2c6a07ca3c6 --- /dev/null +++ b/ui/src/views/iam/DeleteAccountWrapper.vue @@ -0,0 +1,74 @@ +// 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. + + + + + + diff --git a/ui/src/views/iam/DomainActionForm.vue b/ui/src/views/iam/DomainActionForm.vue index ab53a5974e4..8ee185f7e28 100644 --- a/ui/src/views/iam/DomainActionForm.vue +++ b/ui/src/views/iam/DomainActionForm.vue @@ -134,7 +134,7 @@ + + diff --git a/ui/src/views/infra/ChangeStoragePoolScope.vue b/ui/src/views/infra/ChangeStoragePoolScope.vue index 1e1c14201b9..7c1565fe959 100644 --- a/ui/src/views/infra/ChangeStoragePoolScope.vue +++ b/ui/src/views/infra/ChangeStoragePoolScope.vue @@ -68,7 +68,7 @@ diff --git a/ui/src/views/infra/ConfigureHostOOBM.vue b/ui/src/views/infra/ConfigureHostOOBM.vue new file mode 100644 index 00000000000..a873baab83f --- /dev/null +++ b/ui/src/views/infra/ConfigureHostOOBM.vue @@ -0,0 +1,172 @@ +// 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. + + + + + + diff --git a/ui/src/views/infra/Confirmation.vue b/ui/src/views/infra/Confirmation.vue index ea166ac3219..6f9084dc814 100644 --- a/ui/src/views/infra/Confirmation.vue +++ b/ui/src/views/infra/Confirmation.vue @@ -45,6 +45,12 @@ + + + + + + diff --git a/ui/src/views/infra/Resources.vue b/ui/src/views/infra/Resources.vue index 3d0c4a27099..cad1a026c0b 100644 --- a/ui/src/views/infra/Resources.vue +++ b/ui/src/views/infra/Resources.vue @@ -66,7 +66,7 @@ + + diff --git a/ui/src/views/infra/network/DedicatedVLANTab.vue b/ui/src/views/infra/network/DedicatedVLANTab.vue index 7343b30b692..9d47138f0ae 100644 --- a/ui/src/views/infra/network/DedicatedVLANTab.vue +++ b/ui/src/views/infra/network/DedicatedVLANTab.vue @@ -174,7 +174,7 @@ + + diff --git a/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue b/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue index 05c5adc1bd2..f007142fca3 100644 --- a/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue +++ b/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue @@ -195,7 +195,8 @@ optionFilterProp="label" :filterOption="(input, option) => { return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 - }" > + }" + @change="val => { fetchDomainAccounts(val) }"> {{ dom.path }} @@ -213,7 +214,20 @@ :label="$t('label.account')" v-bind="formItemLayout" v-if="isDedicated"> - + + + {{ acc.name }} + + import { ref, reactive, toRaw } from 'vue' -import { api } from '@/api' +import { getAPI } from '@/api' export default { props: { @@ -276,7 +290,8 @@ export default { availableNetworkOfferings: null, ipV4Regex: /^(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)\.(25[0-5]|2[0-4]\d|[01]?\d\d?)$/i, ipV6Regex: /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i, - formModel: {} + formModel: {}, + selectedDomainAccounts: [] }), created () { this.hypervisors = this.prefillContent?.hypervisors || null @@ -454,8 +469,19 @@ export default { hypervisor: [{ required: true, message: this.$t('message.error.hypervisor.type') }] }) }, + fetchDomainAccounts (domainid) { + getAPI('listAccounts', { + domainid: domainid + }).then(response => { + // Clean up the selected account from a previous domain + this.form.account = null + this.selectedDomainAccounts = response.listaccountsresponse.account || [] + }).catch(error => { + this.$notifyError(error) + }) + }, fetchData () { - api('listHypervisors').then(json => { + getAPI('listHypervisors').then(json => { this.hypervisors = json.listhypervisorsresponse.hypervisor if ('listSimulatorHAStateTransitions' in this.$store.getters.apis) { this.hypervisors.push({ name: 'Simulator' }) @@ -465,7 +491,7 @@ export default { }) if (!this.isAdvancedZone || this.securityGroupsEnabled) { - api('listNetworkOfferings', { state: 'Enabled', guestiptype: 'Shared' }).then(json => { + getAPI('listNetworkOfferings', { state: 'Enabled', guestiptype: 'Shared' }).then(json => { this.networkOfferings = {} json.listnetworkofferingsresponse.networkoffering.forEach(offering => { this.setupNetworkOfferingAdditionalFlags(offering) @@ -477,7 +503,7 @@ export default { }) } - api('listDomains', { listAll: true }).then(json => { + getAPI('listDomains', { listAll: true }).then(json => { this.domains = {} json.listdomainsresponse.domain.forEach(dom => { this.domains[dom.id] = dom diff --git a/ui/src/views/network/AclListRulesTab.vue b/ui/src/views/network/AclRulesTab.vue similarity index 98% rename from ui/src/views/network/AclListRulesTab.vue rename to ui/src/views/network/AclRulesTab.vue index e01e61289e5..da6d834e4a3 100644 --- a/ui/src/views/network/AclListRulesTab.vue +++ b/ui/src/views/network/AclRulesTab.vue @@ -25,7 +25,7 @@ :disabled="!('createNetworkACL' in $store.getters.apis)" @click="openAddRuleModal"> - {{ $t('label.add') }} {{ $t('label.aclid') }} + {{ $t('label.add.acl.rule') }} @@ -309,7 +309,7 @@ + + diff --git a/ui/src/views/storage/CreateVolume.vue b/ui/src/views/storage/CreateVolume.vue index 82fcce5c974..26352360c48 100644 --- a/ui/src/views/storage/CreateVolume.vue +++ b/ui/src/views/storage/CreateVolume.vue @@ -168,7 +168,7 @@